Text
                    X.M. Дейтел

П.Дж. Дейтел

БИНОМ


КАК ПРОГРАММИРОВАТЬ НА С
HOW то PROGRAMM S E С О N D H. М. Deiftel Nova University Deitel and Associates P. J. Deitel Deitel and Associates Prentice-Hall International, Inc. E D I T I О N
X. М. Дейтел П. Дж. Дейтел КАК ПРОГРАММИРОВАТЬ НА Перевод с английского под редакцией В. Тимофеева ЗАО Издательство БИНОМ Москва 2000
УДК 004.43 ББК 32.973.26-018.1 Д27 Перевод с английского Бреховских В. B., Карташова B.M., Козлова A.B., Череховского В.И. Харви Дейтел, Пол Дейтел Как программировать БИНОМ», 2000 г. - 1008 на С: Пер. с М.: англ. Книга предлагает читателю курс программирования, С/С++, «Издательство ориентированный мирования, так и на опытных весьма программистов, Помимо достаточно серьезное введение в С++, которые могут просто полного и глубокого из наиболее одного пропустить не изложения языка С перспективных на настоящий момент языков; ему посвящена значительная часть книги. ние на языки и рассчитана как на начинающих, не владеющих никакими языками програм¬ интересующие их главы. дается ЗАО c.: ил. Особое внима¬ объектно-ориентированного программиро¬ вания больших программных систем. Примеры и многочисленные упражнения знако¬ мят читателя с часто применяемыми алгоритмами и фундаментальными структурами данных, показывая технические приемы их реализации. Приводится также масса уделяется методикам структурного и полезных советов. Книга адресована широкому кругу читателей, от новичков до студентов, изучаю¬ щих программирование в рамках своей специальности. Все права защищены. Никакая часть этой книги не форме или любыми средствами, электронными фотографирование, магнитную запись или иные средства информации без письменного разрешения издательства. любой может или быть воспроизведена механическими, копирования или сохранения Authorized translation from the English language edition ISBN 5-7989-0170-X (pyc.) ISBN 0-13-288333-3 (англ.) в включая © Original copyright. Prentice-Hall © Издание на русском языке. ЗАО «Издательство БИНОМ», 2000
Содержание 17 Предисловие Об этой Обзор 18 книге 20 книги Глава 1. Принципы машинной обработки данных 29 1.1. Введение 30 1.2. Что такое 32 компьютер? 1.3. Внутренняя организация 33 компьютера 1.4. Пакетная обработка, мультипрограммирование и разделение времени 1.5. Персональные вычисления, распределенные вычисления и вычисления в модели 1.6. Машинные 1.7. История 34 языка 34 клиент/сервер языки, языки ассемблера и языки высокого уровня 35 36 С 1.8. Стандартная библиотека С 37 1.9. Другие 38 языки высокого уровня 38 1.10. Структурное программирование 39 1.11. Основные принципы среды С 1.12. Общие замечания о С и этой 41 книге 42 1.13. Concurrent С (Параллельный С) 1.14. Объектно-ориентированное программирование Резюме Глава 2. стиль Упражнения Введение в Рекомендуемая литература программирование 2.1. Введение 2.2. Простая программа на С: в С С печать строки текста на С: сложение двух целых чисел понятия о памяти компьютера 2.5. Арифметика на 53 54 2.3. Еще одна простая программа 2.4. Общие 43 программ Упражнения для самоконтроля С++ Советы по программирования Советы по повышению эффективности Ответы к упражнениям для самоконтроля Хороший переносимости и 54 58 63 64
6 Как программировать на С 2.6. Принятие решений: операции равенства и отношения 67 Резюме Распространенные ошибки программирования Хороший Советы по переносимости программ программирования Ответы на упражнения для Упражнения для самоконтроля самоконтроля Упражнения стиль Глава 3. 87 Структурная разработка программ 3.1. Введение 3.2. Алгоритмы 88 88 89 3.3. Псевдокод 3.4. Управляющие структуры 3.5. Структура выбора if 3.6. Структура выбора if/else 90 92 94 3.7. Структура повторения while 3.8. Формулирование алгоритмов: пример 1 (повторение, управляемое счетчиком) 3.9. Формулирование алгоритмов на основе нисходящего 98 99 2 пошагового уточнения: пример 102 (повторение, управляемое контрольным значением) ЗЛО. Формулирование алгоритмов на основе нисходящего пошагового уточнения: пример 3 108 (вложенные управляющие структуры) 113 3.11. Операции присваивания 3.12. Операции инкремента Резюме стиль 114 декремента ошибки программирования Хороший Советы по повышению эффективности Распространенные программирования Общие методические Ответы Глава 4. и к замечания Упражнения для самоконтроля Упражнения упражнениям для самоконтроля 137 Управление программой 4.1.Введени е 138 4.2. Основы структур повторения 139 4.3. Повторение, управляемое 139 счетчиком 4.4. Структура повторения for 141 4.5. Структура for: 144 замечания и рекомендации 145 4.6. Примеры структур for 4.7. Структура со множественным 4.8. Структура повторения 4.9. Операторы break и 148 выбором switch 154 do/while 156 continue 4.10. Логические операции 4.11. Смешивание операций равенства ( ) 4.12. Краткая сводка 158 и присваивания по структурному программированию (=) 161 163
7 Содержание Хороший Распространенные ошибки программирования Советы по повышению эффективности Советы по переносимости программ Общие методические Ответы к замечания Упражнения для самоконтроля Упражнения упражнениям для самоконтроля Резюме стиль программирования Глава 5. 185 Функции 5.1. Введение 186 * 5.2. Программные модули в 187 С 5.3. Функции математической библиотеки 188 5.4. Функции 189 5.5. Определения функций 190 5.6. Прототипы функций 194 5.7. Заголовочные файлы 197 5.8. Вызов функций: 198 вызов по значению и по ссылке 5.9. Генерация случайных 5.10. Пример: 5.11. Классы 199 чисел 203 стохастическая игра 206 памяти 5.12. Правила области действия 209 5.13. Рекурсия 210 5.14. Пример применения рекурсии: 5.15. Рекурсия числа 215 Фибоначчи 219 итерацией Распространенные ошибки программирования Резюме стиль в сравнении с Советы программирования Советы по по переносимости Хороший программ Общие методические эффективности Ответы на Упражнения для самоконтроля для самоконтроля Упражнения повышению замечания упражнения 247 Массивы Глава 6. 6.1.Введени е 248 6.2. Массивы 249 6.3. Объявление 6.5. Передача 6.7. Пример: и с массивами массивов в 6.6. Сортировка 251 массивов 6.4. Примеры работы функции вычисление среднего значения, медианы значения с использованием массивов в массивах 6.9. Многомерные массивы Хорошкй Распространенные ошибки программирования Советы по повышению эффективности Общие методические замечания Упражнения для самоконтроля Ответы к упражнениям для самоконтроля Упражнения Упражнения на рекурсию Резюме стиль 263 267 массивов наиболее вероятного 6.8. Поиск 251 программирования 269 273 277
Как программировать на С 8 Глава 7. 307 Указатели 308 7.1. Введение 7.2. Объявление и инициализация 7.3. Операции 7.4. Передача 309 переменной-указателя 310 над указателями 312 параметра по ссылке 7.5. Использование модификатора const с указателями 7.6. Программа пузырьковой 316 сортировки, использующая 322 вызов по ссылке 7.7. Выражения и арифметические операции 7.8. Связь между указателями с указателями 329 и массивами 333 7.9. Массивы указателей 7.10. Пример: программа 7.11. Указатели Резюме на 326 334 тасовки и сдачи колоды карт 339 функции ошибки программирования Хороший Советы по повышению эффективности Распространенные стиль программирования Советы по переносимости Общие методические программ Ответы на Упражнения для самоконтроля Специальный Упражнения упражнения для самоконтроля раздел: как самому построить компьютер замечания Глава 8. Символы и 369 строки 370 8.1. Введение 8.2. Строки 370 и символы 8.3. Библиотека обработки 372 символов 377 8.4. Функции преобразования строк 382 8.5. Функции стандартной библиотеки ввода/вывода 8.6. Функции операций над строками из библиотеки 385 обработки строк 8.7. Функции сравнения 8.8. Функции поиска из 8.9. Функции памяти 8.10. Другие функции Резюме стиль 388 библиотеки обработки строк 390 библиотеки обработки строк 395 библиотеки обработки строк из 399 библиотеки обработки строк ошибки программирования Хороший Советы по переносимости программ Ответы к упражнениям для самоконтроля Распространенные программирования Упражнения для самоконтроля упражнения Глава 9. из по Специальный раздел: более Упражнения работе со строками 419 Форматированный ввод/вывод 420 9.1. Введение 421 9.2. Потоки 9.3. Форматированный вывод 9.4. Печать сложные целых чисел с применением printf 421 422
9 Содержание плавающей точкой 9.5. Печать чисел с 9.6. Печать строк 423 425 и символов 9.7. Другие спецификаторы преобразования 426 9.8. Печать 428 с заданием ширины поля и точности представления 9.9. Использование флагов 9.10. Печать литералов Резюме стиль printf строке управления форматом 430 Еэс-последовательностей 433 ввод с применением scanf 434 и 9.11. Форматированный в ошибки программирования Хороший Советы по переносимости программ Ответы на упражнения для самоконтроля Распространенные программирования Упражнения для Упражнения самоконтроля Глава 10. Структуры, объединения, операции и с битами 451 перечисления 10.1. Введение 452 10.2. Описания структур 453 10.3. Инициализация структур 455 10.4. Доступ 455 к элементам структур 10.5. Использование структур с функциями 457 10.6. 458 Typedef 10.7. Пример: моделирование высокоэффективной тасовки 459 и раздачи карт 10.8. Объединения 461 10.9. Поразрядные операции 463 10.10. Битовые 471 поля 10.11. Перечислимые 474 константы Резюме Хороший Распространенные ошибки программирования Советы по переносимости программ программирования Советы по повышению эффективности Общие методические Ответы на замечания Упражнения для самоконтроля упражнения для самоконтроля Упражнения стиль Глава 11. Работа с 489 файлами 11.1. Введение 490 11.2. Иерархия данных 490 11.3. Файлы 493 и потоки 11.4. Создание файла последовательного доступа 493 11.5. Чтение данных 499 из файла последовательного доступа 503 11.6. Файлы произвольного доступа 11.7. 504 Создание файла произвольного доступа 11.8. Произвольная запись данных в 11.9. Последовательное доступа файл произвольного чтение данных из файла доступа 506 произвольного 508
ТО Как программировать на С 11.10. Резюме стиль 508 Пример: программ^обработкитранзакций Распространенные программирования Советы по повышению самоконтроля Упражнения ошибки программирования Советы по переносимости Упражнения для эффективности Ответы на Хороший программ упражнения для самоконтроля Глава 12. Структуры данных 12.1. Введение 12.2. Структуры, 12.3. 527 528 ссылающиеся на себя Динамическое распределение 12.4. Связанные 529 530 памяти 531 списки 12.5.Стек и 539 12.6. Очереди 545 12.7. Деревья 550 Резюме стиль Распространенные программирования ошибки программирования Советы по повышению Хороший эффективности Советы по переносимости программ Упражнения для самоконтроля Ответы на упражнения для самоконтроля Упражнения Глава 13. Препроцессор 587 13.1. Введение 13.2. Директива препроцессора #include 588 13.3. препроцессора #define: символические константы 589 Директива 588 13.4. Директива препроцессора #define: макросы 590 13.5. Условная компиляция 592 13.6. Директивы препроцессора #error 13.7. Операции # и и 593 #pragma 593 ## 13.8. Нумерация строк 13.9. Предопределенные 594 594 символические константы 13.10. Макрос подтверждения Резюме стиль 594 ошибки программирования Хороший Советы по повышению эффективности Ответы на упражнения для самоконтроля Распространенные программирования Упражнения для самоконтроля Упражнения Глава 14. Специальные вопросы 14.1. Введение 14.2. Переадресация ввода/вывода 601 602 в системах UNIX и DOS 602 14.3. Списки аргументов переменной длины 603 14.4. Аргументы командной строки 606 14.5. Замечания относительно компиляции программ из нескольких исходных файлов 607
11 Содержание 14.6. Выход из программы с помощью 14.7. Модификатор типа exit и atexit 609 volatile 14.8. Суффиксы для целых 609 констант и констант плавающей точкой 610 14.9. Еще раз о файлах 14.10. Обработка сигналов 611 с 613 14.11. Динамическое выделение памяти: функции calloc и realloc 14.12. Безусловный переход: goto 615 616 Советы по Распространенные ошибки программирования Советы по повышению эффективности программ Общие методические замечания Упражнения для самоконтроля Ответы на упражнения для самоконтроля Упражнения Резюме переносимости Глава 15. С++ «улучшенный» С как 625 15.1. Введение 626 15.2. Однострочные комментарии С++ 627 15.3. Потоковый ввод/вывод С++ 15.4. Объявления 15.5. Создание в 628 С++ 630 новых типов данных в 15.6. Прототипы функций и С++ 630 контроль соответствия типов 631 15.7. Встроенные функции 633 15.8. 637 Параметры-ссылки 15.9. Модификатор const 641 15.10. Динамическое распределение памяти delete 643 15.11. Параметры, используемые по умолчанию 15.12. Унарная операция разрешения области действия 644 с помощью new и 646 647 15.13. Перегрузка функций 15.14. Спецификации внешней 649 связи 15.15. Шаблоны функций Резюме стиль 649 Распространенные программирования ошибки программирования Советы по переносимости Хороший программ Общие методические эффективности Ответы к Упражнения для самоконтроля упражнениям для самоконтроля Упражнения Рекомендуемая Приложение: ресурсы С++ литература Советы по повышению замечания Глава16. Классы и абстракция данных 663 16.1. Введение 664 16.2. 666 16.3. Определение структур Доступ к элементам структур 16.4. Реализация пользовательского типа с помощью структуры 667 Time 668
12 Как программировать на С 16.5. Реализация абстрактного типа данных Time 670 с помощью класса 16.6. Область действия 16.8. 676 класса и доступ к элементам класса 16.7. Отделение интерфейса 677 от реализации доступом к элементам класса Управление 681 Функции доступа и сервисные функции 16.10. Инициализация объектов класса: конструкторы 684 16.11. Использование 688 16.9. 687 с конструкторами аргументов по умолчанию 16.12. Деструкторы 691 16.13. Когда 691 вызываются конструкторы и деструкторы 16.14. Использование элементов данных и 16.15. Скрытая ловушка: возвращенце закрытый на 694 элементов-функций- ссылки 699 элемент данных 16.16. Присваивание по умолчанию путем поэлементного 701 копирования 16.17. Повторное использование программного 701 обеспечения Распространенные ошибки программирования Хороший Советы по повышению эффективности Общие методические замечания Упражнения для самоконтроля Ответы к упражнениям для самоконтроля Упражнения Резюме стиль программирования Глава 17. Классы: часть II 715 17.1. Введение 716 17.2. Константные объекты 17.3. Композиция: и константные элементы-функции* классы в качестве элементов других классов 17.4. Дружественные функции и дружественные классы 17.6. Динамическое распределение new и delete 17.8. Абстракция данных 17.10. Шаблоны Резюме памяти с помощью 723 727 операций 734 735 элементы класса 17.9. Контейнерные 717 729 17.5. Указатель this 17.7. Статические * и сокрытие 739 информации 743 классы и итераторы 743 классов ошибки программирования Хороший Советы по повышению эффективности Распространенные стиль программирования Советы по переносимости программ Общие методические Упражнения для Ответы самоконтроля Упражнения упражнениям для самоконтроля замечания Глава 18. Перегрузка операций 18.1. Введение 18.2. Основные принципы перегрузки операций к 755 756 757
13 Содержание 18.3. Запреты на перегрузку операций 18.4. Функции-операции как элементы и как дружественные функции 18.5. Перегрузка операций передачи 759 класса 760 в поток 762 и извлечения из потока 18.6. Перегрузка одноместных операций 764 18.7. Перегрузка двухместных операций 765 18.8. Пример: Array 18.9. Преобразование типов 765 18.10. Пример: 777 класс 18.11. Перегрузка 18.12. Пример: Резюме стиль ++ и 786 - Date класс 788 Распространенные Ответы к ошибки программирования Советы программирования Общие методические 776 String класс по повышению Хороший эффективности Упражнения для самоконтроля Упражнения замечания упражнениям для самоконтроля Глава 19. Наследование 19.1. Введение 19.2. Базовые и 805 806 808 производные классы 19.3. Защищенные 810 элементы 19.4. Приведение указателей базового к указателям на производный класса 811 класс 815 19.5. Применение функций-элементов 19.6. Переопределение базового элементов класса 816 в производном классе 19.7. Открытые, защищенные 19.8. Непосредственные и закрытые и косвенные 19.9. Применение конструкторов базовые базовые классы и деструкторов 821 в производном классе 19.10. Неявное преобразование объектов производного к базовому 19.11. Наследование 19.12. Композиция 820 820 классы класса 824 в конструировании программного обеспечения в сравнении с наследованием 19.13. Отношения «использует» и «знает» 825 827 828 19.14. Пример: Point, Circle, Cylinder 828 19.15. Сложное наследование 833 Резюме стиль ошибки программирования Хороший Совет по повышению эффективности Распространенные программирования Общие методические замечания Упражнения для самоконтроля Упражнения Ответы на упражнения для самоконтроля
Как программировать на С 14 Глава 20. Виртуальные функции и 847 полиморфизм 848 20.1. Введение 20.2. Обработка различных типов данных при помощи 849 операторов switch 849 20.3. Виртуальные функции 20.4. Абстрактные базовые классы 850 и конкретные классы 851 20.5. Полиморфизм 20.6. Пример: программа начисления использующая возможности 20.7. Новые заработной платы, 853 полиморфизма 862 классы и динамическое связывание 864 20.8. Виртуальные деструкторы 20.9. Пример: наследование интерфейса 864 и реализации Резюме стиль ошибки программирования Хороший Советы по повышению эффективности Распространенные программирования Общие методические Ответы на Упражнения для самоконтроля Упражнения замечания упражнения для самоконтроля Глава 21. Потоки ввода/вывода в 877 С++ 21.1. Введение 879 21.2. Потоки 879 21.3. Потоковый вывод 882 21.4. Потоковый ввод 886 21.5. Функции неформатируемого ввода/вывода read, gcount и write 892 21.6. Манипуляторы 893 потоков 21.7. Флаги форматирования 21.8. Состояния ошибки 21.9. Ввод/вывод определяемых 21.10. Привязка 896 потока 906 потоков 907 пользователем типов 910 потока вывода к потоку ввода Хороший Распространенные ошибки программирования Советы по повышению эффективности Общие методические замечания Упражнения для самоконтроля Ответы на упражнения для самоконтроля Упражнения Резюме стиль программирования Приложение 929 А. Синтаксис С 941 Приложение Б. Стандартная библиотека Приложение В. Таблица приоритета операций Приложение Г. Набор символов ASCII . 978 980
15 Содержание Приложение Д. Системы Д.1. Введение Д.2. Запись двоичных 981 счисления 982 чисел в виде восьмеричных и шестнадцатеричных 985 Д.З. Преобразование восьмеричных и шестнадцатеричных чисел в двоичные 987 двоичной, восьмеричной Д.4. Преобразование или шестнадцатеричной форм в десятичную числа из Д.5. Преобразование десятичного числа в двоичное, восьмеричное 988 или шестнадцатеричное Д.6. Отрицательные двоичные Резюме числа: дополнение до двух Упражнения для самоконтроля Упражнения для самоконтроля 987 Ответы на упражнения 990

Предисловие книгу написали два человека Эту мирует и учит старый программировать других и молодой. Старик програм¬ лет. Молодой человек больше 30-ти работал программистом около десяти лет, йока им не овладела страсть к учи¬ тельству и писательству. Старик программирует и учит благодаря своему опы¬ ту. Молодой человек благодаря неистощимому запасу энергии. Старик стре¬ мится к ясности; молодой человек к наивысшей эффективности. Старику и элегантность. Молодому человеку хочется результатов. Мы объединились, чтобы на свет появилась книга, которую, как мы надеемся, вы найдете полезной, интересной и занимательной. нравится красота В большинстве учебных курсов языку С учат людей, которые знают, как программировать. Многие преподаватели думают, что сложность С и ряд дру¬ гих трудностей делают этот язык непригодным для начального граммированию как раз для того, на что нацелена эта книга. написали? Язык С занял особое обучения про¬ Так почему же мы ее ной индустрии, место среди языков реализации систем в компьютер¬ объектно-ориентирован¬ ный вариант С++ станет ведущим языком середины конца 90-х годов. Хар¬ ви Дейтел в течение 13-ти лет преподавал в университетах Паскаль, делая ак¬ цент на разработке ясно написанных, хорошо структурированных программ. Большинство и есть все основания верить, что его риал в точности так, как это делал Тут курсах. старший это основ¬ этот мате¬ из нас в своих университетских имеются определенные ловушки, но там, где они встречаются, мы указываем на них и можем Паскалю Мы представили из того, чему учат во вводных курсах по ные принципы структурного программирования. сказать, что объясняем, как можно их избежать. Исходя из опыта мы обучающиеся справляются с данным курсом ничуть не хуже, чем с аналогичным, но ориентированным на Паскаль. Однако здесь есть одно существенное отличие: у студентов появляется очень сильный стимул благодаря тому, что они учат язык, который немедленно окажется им необхо¬ дим, как только они покинут стены университета. Они с энтузиазмом относят¬ ся к работе с материалом что немаловажно, учитывая повышенную труд¬ С. Наша цель была ясна: написать учебник по программированию на С для вводных курсов университетского уровня с ориентацией на студентов, имею¬ щих небольшой или вообще нулевой опыт программирования, но одновремен¬ ность изучения но и книгу, предлагающую достаточно строгое изложение теории и практики, С. Чтобы достичь этих целей, обычные учебники, потому что характерное для традиционных курсов по языку мы написали книгу более объемистую, чем наш текст, помимо всего прочего, постепенно приучает читателя к принципам структурного программирования. Около 1000 студентов изучали предлагае¬ мый здесь материал на наших курсах. изучали С по первому изданию этой Десятки тысяч учащихся по всему миру книги. Книга содержит большой набор примеров, упражнений личных областей, чтобы и проектов из раз¬ дать читателю возможность решать интересные зада¬ чи из реального мира. Книга сосредоточивает внимание на принципах качественного конструи¬ рования программного обеспечения и делает акцент на ясности программ, на¬ писанных с использованием методологии структурного программирования. Мы пытаемся избегать сиса в пользу обучения эзотерической терминологии на примерах. и спецификаций синтак¬
18 Как программировать на С Среди педагогических элементов, применяемых в тексте законченные демонстрирующие обсуждаемые концеп¬ ции; краткое содержание, приведенное в начале каждой главы; распростра¬ ненные ошибки и правила хорошего стиля программирования, сообщаемые по программы и примеры их вывода, отдельный раздел в конце главы; резюме и кон¬ трольные вопросы с ответами к ним; и наконец, богатейший набор упражне¬ ний, какой вы вряд ли найдете в других книгах по С. Упражнения варьируют¬ ся от простых вопросов на повторение до больших программных задач и насто¬ ходу изложения и сведенные в ящих полноценных проектов. Преподавателям, которым требуются достаточ¬ работ для студентов, найдут много но сложные проекты в качестве курсовых подходящих приложили курс задач, приведенных в упражнениях приобрел для студента в главах 5-й по 21-ю. Мы благодаря упражнениям данный большую ценность. Программы в тексте книги те¬ немалые усилия к тому, чтобы ANSI компиляторов на Sun SPARCstation, Apple Macintosh (Think С), IBM PC (Turbo С, Turbo С++ и Borland С++) и DEC VAX VMS (VAX С). Данная книга следует стандарту ANSI С. Многие особенности ANSI С не реализуются компиляторами для более ранних версий С. Обратитесь к руко¬ водствам по вашей конкретной системе, чтобы выяснить больше подробностей относительно языка, либо достаньте документ ANSI/ISO 9899: 1990, «Ameri¬ can National Standard for Information Systems С». Programming Language стировались при помощи совместимых с Об этой Все то, компоненты и отличительные чтобы помочь студентам книге особенности этой книги направлены на в изучении языка. Цели урока Каждая глава начинается с формулирования учебных задач. Благодаря этому студент получает представление о том, к чему следует подготовиться, а также, по прочтении главы, он может оценить, насколько ему удалось выпол¬ нить данные задачи. Содержание глав Содержание в начале каждой главы помогает студенту подойти к изуче¬ «сверху вниз». Тем самым он может видеть, что спланировать свой темп работы. нию материала, так сказать, ждет его впереди, Разделы Каждая глава и организована ключевым моментам темы. в виде Каждый небольших разделов, посвященных С представлен в контексте закон¬ аспект ченной работающей программы. После каждой программы приводится «окно», содержащее вывод, полученный при ее запуске. Это позволит студен¬ ту убедиться, что программа работает, как ожидалось. Соотнесение вывода с операторами программы, которые его производят, является превосходным подспорьем в изучении и закреплении принципиального материала. Наши разработаны С. Внимательное чтение программы мы действительно для того, чтобы испытать разнообразие возможностей будто эти програм¬ книги должно выглядеть так, как вводятся, компилируются и запускаются на компьютере.
19 Предисловие Иллюстрации Книга содержит разнообразные рисунки и диаграммы. Обсуждение струк¬ турных блок-схем, помогающее оценить значение управляющих структур и структурного программирования, сопровождается ясными диаграммами. Гла¬ ва о структурах данных содержит массу рисунков, иллюстрирующих создание и поддержку связанных списков, очередей, стеков и двоичных деревьев. Вспомогательные Мы используем оформления оформления, чтобы помочь студентам важных аспектах разработки программ, их те¬ элементы пять элементов сконцентрировать внимание на стирования и отладки, вопросах эффективности и переносимости. Мы выделя¬ ем соответствующую информацию под рубриками Хороший стиль программи¬ рования, нию Распространенные ошибки программирования, Советы по повыше¬ по переносимости кода и Общие методические эффективности, Советы замечания. Хороший В тексте стиль программирования советы выделяются относительно Они привлекают внимание лучшие программы. Эти советы граммирования. щим писать что мы вынесли из своего сорокалетнего Распространенные язык ния к этим распространенным Лучше стиля приемам, сообщают самое (в сумме) опыта. дсобенно про¬ помогаю¬ ценное из того, те, кто проходит начальный курс склонны совершать одни и те же отношении. к ошибки программирования Студенты, изучающие обучения, целесообразного читателя ошибки. Привлечение ошибкам программистов внима¬ очень помогает в этом учиться на чужих ошибках, чем на своих это позволяет избежать напрасной потери времени. Советы Мы по повышению эффективности считаем, что написание ясных и понятных программ целей начального курса по программированию. самые быстрые, ком-либо из хотят писать самые компактные, самые экономные, либо выдающиеся в ка¬ ином отношении программы. Им действительно небезразлична эф¬ фективность программ. Они хотят знать, мы важнейшая Однако студенты как можно оснастить свои програм¬ каким-нибудь «усилителем». Поэтому мы включили в текст Советы по по¬ эффективности, чтобы подчеркнуть возможные пути улучшения вышению программ. Советы по переносимости программ Разработка программного обеспечения сложное и чрезвычайно дорогое Организации, разрабатывающие программы, часто вынуждены про¬ изводить различные их версии, приспособленные к разнообразным существу¬ ющим компьютерам и операционным системам. Поэтому сегодня делается си¬ занятие. льный акцент на переносимости, т.е. на производстве такого программного обеспечения, которое будет работать без изменений на многих компьютерных системах. Многие утверждают, что С лучший язык для разработки перено¬ симого программного обеспечения. зуют свою программу на сту ошибочно. льного С, Некоторые полагают, что если они реали¬ она автоматически будет переносимой. Это попро¬ Достижение переносимости требует тщательного и осмотрите¬ проектирования. Здесь масса ловушек. В самом документе ANSI по
20 Как программировать на С Стандартному С перечень потенциальных трудностей занимает 11 страниц. Мы включили в текст многочисленные Советы по переносимости кода, в ко¬ торых обобщили свой собственный опыт разработки переносимого програм¬ много обеспечения, дополнив его внимательным изучением раздела стандарта ANSI, посвященного переносимости. Общие Этот под этой методические элемент замечания новый оформления рубрикой во втором издании. Мы суммировали замечания, касающиеся архитектуры и конструирования программных систем, особенно больших систем. Резюме Каждая из глав оканчивается набором дополнительных разделов, при¬ званных служить определенным педагогическим целям. Мы приводим деталь¬ ное резюме главы в форме перечня ключевых пунктов. Это помогает студенту пересмотреть и закрепить в памяти важнейшие моменты материала. Терминология Раздел Терминология содержит список важнейших ном порядке), определяемых в данной главе. Цель его терминов (в алфавит¬ та же закрепление пройденного материала. Затем мы приводим все имеющиеся в главе вставки, собранные по рубрикам Хороший стиль программирования, Распростра¬ ненные ошибки программирования, Советы по переносимости кода Упражнения Эти и Советы по повышению Общие методические эффективности, замечания. для самоконтроля многочисленные упражнения, с исчерпывающими ответами на них, включены в книгу для самостоятельного изучения. приобрести уверенность чтобы попробовать в применении изученного Они и позволяют студенту подготовиться к тому, свои силы в решении настоящих задач. Упражнения Завершает каждую главу объемистый набор упражнений, диапазон кото¬ рых меняется от простого повторения важнейших терминов и понятий, через написание одиночных операторов C,, небольших фрагментов функций, закон¬ ченных функций и программ Такое большое число свои курсы к до написания больших курсовых проектов. упражнений позволяет преподавателям специфическим потребностям аудитории и менять приспособить учебные зада¬ ния от семестра к семестру. Они могут использовать эти упражнения в качестве домашних заданий, для коротких опросов и для экзаменационных сессий. Обзор книги Первая из них, охватыва¬ подробное изложение языка С, включая формальное введение в структурное программирование. Вторая главы с 15-й по часть что отличает нашу книгу от других учебников по С, 21-ю, представляет собой серьезное введение в язык С++ и объектно-ориенти¬ рованное программирование, пригодное в качестве учебного пособия для сту¬ Книгу ющая можно разделить на три основных части. главы с 1-й по 14-ю, содержит очень
21 Предисловие Третья часть, приложения А Д, содержат материал, дополняющий основной текст. Глава 1, «Введение», рассказывает о том, что такое компьютеры, как они дентов старших курсов колледжа- разнообразный справочный работают и как их программирования программируют. и В ней вводится объясняется, почему переход понятие структурного к структурным методи¬ написания программ. В главе кам явился своего рода революцией в практике дается краткая история развития языков программирования от машинных языков через языки ассемблера к языкам высокого уровня. Рассказано об истоках языка вания С. В главе также представлено введение в среду программиро¬ С. Глава 2, «Введение в программирование на С», дает общее представление о том, как пишутся программы на С. Приводится подробное описание принятия решений и арифметических операций. После прочтения главы учащийся бу¬ дет понимать, как написать простую, но тем не менее законченную программу на С. Глава 3, «Структурное программирование», является, возможно, наибо¬ лее важной во всей книге, особенно для студента, всерьез занимающегося компьютерными дисциплинами. В ней вводится понятие алгоритмов (проце- решения задач. Объясняется важность структурных методов для написа¬ ния понятных, простых в отладке и сопровождении программ, которые с боль¬ шей вероятностью могут правильно заработать с первой попытки. Вводятся ДУР) фундаментальные управляющие конструкции структурного программирова¬ ния, а именно последовательная, выбора (if и if/else) и повторения (while). Объясняется методика нисходящего пошагового уточнения, критическая для написания хорошо структурированных программ. Обсуждается популярный инструмент проектирования программ структурный псевдокод. Методы и подходы, изложенные в 3-й главе, приложимы к структурному программиро¬ ванию на любом языке, не только на С. Глава помогает учащемуся выработать «хорошие привычки» в программировании, заранее подготавливая его к более серьезным программным задачам в оставшейся части текста. Глава 4, «Управление программой», уточняет понятия структурного про¬ граммирования и представляет дополнительные управляющие структуры. Де¬ тально исследуется повторение и сравниваются альтернативы циклов, ляемых счетчиком, и циклов, управляемых контрольным значением. управ¬ Вводит¬ for как удобное средство реализации циклов с управлением счет¬ Представляются структура выбора switch и структура повторения do/while. Заканчивает главу обсуждение логических операций. Глава 5, «Функции», обсуждает проектирование и построение програм¬ ся структура чиком. мных модулей. В С имеются стандартные библиотечные функции и функции, определяемые программистом, возможности рекурсии и вызова по значению. Методики, представленные в 5-й главе, существенны для написания и пони¬ мания преимуществ правильно структурированных программ, ших программ и вообще того рода программного ным программистам скорее всего придется особенно боль¬ обеспечения, которое разрабатывать систем¬ приложений ре¬ ального мира. В качестве эффективного средства для решения сложных задач предлагается стратегия «разделяй и властвуй»; функции для позволяют програм¬ мисту разбить сложную программу на более простые взаимодействующие ком¬ поненты. Студентам нравится заниматься случайными числами и моделирова¬ поэтому их увлекает раздел главы, обсуждающий игру в кости, где изящно используются управляющие структуры. Глава также предлагает осно¬ вательное введение в рекурсию; приводится таблица, где перечислены приме¬ нием,
22 Как программировать на С ры и упражнения на рекурсию (всего их 31), встречающиеся на протяжении остальной части книги. В некоторых учебниках рекурсия вводится в ка¬ кой-либо из последних глав; но мы считаем, что эту тему лучше всего охваты¬ вать постепенно, на в конце упражнений всем 5-й ные задачи, например, текста. протяжении В объемистый набор из 39 главы включены некоторые классические рекурсив¬ Ханойская башня. Глава 6, «Массивы», обсуждает структурирование данных в виде масси¬ вов, т.е. групп логически связанных элементов одного типа. В главе представ¬ лены многочисленные примеры как одномерных, так и двумерных массивов. Структурирование данных повсеместно признается не менее важным момен¬ том разработки структурных программ, чем применение управляющих струк¬ тур. Примеры в главе исследуют различные распространенные операции над массивами, печать гистограмм, сортировку данных, передачу массивов функ¬ циям и дают представление об анализе наблюдаемых данных. Следует отме¬ подробное рассмотрение тить метода двоичного поиска, как важнейшего шага вперед по сравнению с линейным поиском. чают себя особенно много интересных в улучшение методов авиабилетов, сортировки, LOGO), трудных проектирование введение в концепцию нитой в качестве языка Упражнения и в конце главы вклю¬ задач. системы Сюда относятся резервирования «черепаховой графики» (ставшую а также задачи об обходе доски знаме¬ конем и о вось¬ ферзях, где вводятся понятия эвристического программирования, столь широко используемые в области исследования искусственного интеллекта. Глава 7, «Указатели», представляет одну из самых мощных особенностей ми языка С. Глава обсуждает операции, применяемые с указателями, арифметику указателей, взаимо¬ массивов, массивы указателей и указатели на функции. В в деталях вызов по ссылке, выражения с указателями, указателей и упражнений главы входит моделирование классического соревнования между зайцем и черепахой, а также алгоритмы тасования и сдачи карт. Име¬ связь число специальный раздел, озаглавленный «Как построить свой собственный компьютер». В нем объясняются принципы программирования на машинном языке и представлен проект, включающий в себя проектирование и реализа¬ ется цию компьютерного симулятора, позволяющего читателю писать и запускать программы на «машинном» языке. Этот раздел будет особенно интересен для работает компьютер. Нашим студен¬ там очень нравится этот проект, и они часто предлагают и реализуют сущест¬ венные его усовершенствования; некоторые из них предлагаются читателю в качестве упражнений. Другой специальный раздел имеется в главе 12, где чи¬ тех, кто хочет понять, как на самом деле тателю демонстрируется создание компилятора; машинный код, генерируе¬ мый компилятором, затем исполняется на симуляторе, разработанном в 7-й главе. В главе 8, «Символы и строки», речь идет ботки нечисловых данных. В главу включен ботки символов мые здесь, и строк, имеющихся в об основных принципах обра¬ подробный обзор функций обра¬ библиотеках С. Методики, обсуждае¬ широко используются при создании текстовых редакторов, графских программ макетирования и приложений обработки текстов. типо¬ Глава завершается интересным набором из 33 упражнений, исследующих обработку Читателю понравятся задачи, где требуется писать лимерики и стоха¬ стические стихотворения, переводить английский текст на «свинячью ла¬ текстов. семибуквенные слова, эквивалентные заданному номеру осуществлять выравнивание текста, защищать чеки, записывать сумму чека прописью, генерировать код Морзе, делать метрические преобра¬ тынь», генерировать телефона,
23 Предисловие зования и отправлять угрожающие письма с напоминаниями об уплате. Нако¬ нец, последнее упражнение предлагает даже использовать компьютерный сло¬ кроссворды! Глава 9, «Форматированный ввод/вывод», рассатривает мощные возмож¬ ности функций printf и scanf. Мы обсуждаем различные детали форматирова¬ ния вывода с помощью printf, такие как округление значений с плавающей точкой до заданного количества значащих цифр, выравнивание столбцов чи¬ сел, левое и правое выравнивание в поле, вставка литеральной информации, принудительный вывод знака «плюс», печать начальных нулей, использова¬ варь для того, чтобы генерировать ние экспоненциальной нотации, восьмеричных и шестнадцатеричных чисел и шириной поля и точностью представления. Приводятся все esc-noследовательности функции printf для перемещения курсора, вывода специа¬ управление льных символов и подачи звукового сигнала. Затем исследуются все возмож¬ ности функции scanf, ввода с помощью форматного включая ввод ских типов данных и пропуск символов во входном потоке. спецификаторы формата scanf для специфиче¬ Мы обсуждаем все чтения десятичных, восьмеричных, шест¬ плавающей точкой, символов и строк, а так¬ же для сканирования ввода на предмет поиска совпадений (или несовпадений) с заданным набором символов. Упражнения главы демонстрируют практиче¬ ски все возможности форматирования в С. надцатеричных значений, чисел с Глава 10, «Структуры, объединения, операции представляет широкое поминают «записи» в разнообразие Паскале и других языках группируют элементы данных различных типов. льзуются для В главе лением формирования файлов, состоящих 12 структуры памяти с битами из область для Такое разделение ричного хранения удобных создания динамических структур данных, Объединения памяти позволяет сократить объему данных. Перечисления памяти или к пространству на позволя¬ требования про¬ устройствах вто¬ представляют собой средство опре¬ в использовании символических констант; такие константы помогают сделать программу С испо¬ памяти в разные моменты времени для данных раз¬ граммы к доступному деления на¬ они информационных записей. таких как связанные списки, очереди, стеки и деревья. личных типов. Структуры программирования В главе 11 структуры в соединении с указателями и динамическим распреде¬ применяются ют использовать и перечисления» важных элементов языка. самодокументированной. Мощные в плане манипуляции отдельными битами возможности позволяют программистам писать использующий низкоуровневые особенности оборудования. Программы обрабатывать битовые последовательности, устанавливать или сбрасы¬ вать отдельные биты и хранить информацию более компактно. Эти черты С, свойственные обычно только языкам ассемблера, чрезвычайно ценятся про¬ код, могут граммистами, пишущими системное обеспечение, например, операционные системы или сетевое программное обеспечение. Главным примером этой главы является пересмотренная, высокоэффективная модель тасования и сдачи карт. Он дает преподавателю прекрасную возможность продемонстрировать, что такое качество алгоритма. Глава 11, «Обработка файлов», обсуждает методы обработки текстовых произвольном режимах доступа. Глава начина¬ ется с представления иерархии данных от отдельных битов к байтам, к полям, к записям и, наконец, к файлам. Затем показан простой подход языка С к файлов в последовательном и файлам и потокам. файлам обсуждается в контек¬ файлы открываются и закрыва¬ последовательный файл и затем чтение из Последовательный доступ сте трех программ, которые показывают, как ются, как производится запись в к
24 Как программировать на С него. Произвольный грамм, файл доступ к как, файлам обсуждается йа примере четырех про¬ используя последовательный доступ, создать дальнейшей работы с произвольным доступом к нему, и как произ¬ показывающих, для водится последовательное чтение файла Четвертый с произвольным доступом. пример для демонстрации произвольного доступа комбинирует различные приемы как последовательного, так и произвольного доступа к файлам, явля¬ ясь законченной программой обработки транзакций. Студенты на наших про¬ мышленных семинарах говорили, что после изучения материала по файлов обработке они смогли писать серьезные программы, сразу оказывавшиеся полез¬ работе их организаций. Глава 12, «Структуры данных», ными в рассматривает создание динамических структур. Глава начинается с представления структур, ссылающихся на себя, и динамического распределения памяти. Затем обсуждается, как создавать поддерживать различные динамические структуры данных, и включая связан¬ Для каждого типа структур мы приво¬ работающие программы и показываем, что выводится в ре¬ зультате их работы. Глава 12 помогает студенту в совершенстве овладеть прие¬ мами работы с указателями. В главе имеется масса примеров с косвенной ад¬ ные списки, очереди, стеки и деревья. дим законченные и ресацией Одной из двойной косвенной адресацией проблем работы зуально представить Поэтому особенно трудной конструкцией. с указателями является то, что студентам трудно ви¬ себе структуры данных и связи их узлов друг с другом. мы приводим иллюстрации, показывающие эти связи и последовате¬ которой они создаются. Пример с двоичным деревом завершает изу¬ указателей и динамических структур данных. Этот пример создает дво¬ ичное дерево, исключая при этом дублирование данных, и производит рекур¬ сивные обходы дерева с предварительной, порядковой и отложенной выбор¬ кой. Студенты получают настоящее удовольствие от изучения и реализации этого примера. Особенное впечатление на них производит тот факт, что обход дерева с порядковой выборкой печатает значения узлов в сортированном виде. Завершает главу большое собрание упражнений. Важнейшие из них объеди¬ льность, в чение няются в специальном разделе «Как построить свой собственный компиля¬ Упражнения проводят студента через процесс разработки программы преобразования инфиксной нотации в постфиксную и программы постфикс¬ ной оценки. Затем мы модифицируем алгоритм постфиксной оценки так, что¬ бы он генерировал код машинного языка. Компилятор помещает этот код в файл (используя методы главы 11). Затем студенты запускают машинный код, генерируемый их компилятором, на программном симуляторе, построенном в тор». упражнениях 7-й главы. Глава 13, «Препроцессор», дает подробные описания директив препроцес¬ сора. Включена более полная информация по директиве #include, которая приводит к включению перед компиляцией копии указанного файла вместо #define, создающей символические константы и мак¬ обсуждается условная компиляция, позволяющая программи¬ директивы, и директиве росы. В главе сту управлять исполнением препроцессорных директив и компиляцией кода программы. Также описывается операция #, преобразующая свой операнд в строку, и операция ##, выполняющая конкатенацию двух лексем. Представ¬ FILE ( LINE ). Наконец, обсуждается макрос assert лены пять предопределенных символических констант DATE , TIME и STDC , , из файла assert.h. Этот макрос является ценным инструментом те¬ стирования, отладки и верификации программ. заголовочного
25 Предисловие Глава 14, «Специальные вопросы», посвящена довольно сложной темати¬ Раздел 14.2 по¬ файл и направить в вход другой (конвейер), ке, которая обычно не затрагивается во вводных курсах по С. казывает, как можно переадресовать ввод программы на файл ее вывод, направить вывод одной программы на присоединить вывод программы в конец существующего файла. Раз¬ 14.3 описывает разработку функций со списками аргументов переменной дел длины. Раздел 14.4 показывает, как передать функции main аргументы а также командной строки. Раздел 14.5 говорит о компиляции программ, компоненты которых распределены по различным файлам. Раздел 14.6 обсуждает регист¬ рацию с помощью atexit функций, которые должны вызываться при заверше¬ завершение исполнения программ вызовом функции exit. В разделе 14.7 описываются модификаторы типа const и volatile. Раздел 14.8 показывает, как специфицируетСя тип числовых констант m средством нии программы, а также целых и с суффиксов файлы и использование пользовать плавающей запятой. Раздел 14.9 обсуждает двоичные файлов. В разделе 14.10 показано, как ис¬ временных библиотеку обработки Наконец, раздел денные события. сигналов, чтобы перехватывать непредви¬ 14.11 посвящен созданию и использованию функций calloc и realloc. В первое издание книги мы включили одну главу, являвшуюся введением С++ и объектно-ориентированное программирование. За время, прошедшее с динамических массивов с помощью в тех пор, многие университеты решили дополнить свои курсы по С введением в С++. Поэтому в новом издании мы расширили наше изложение С++ и объект¬ но-ориентированного программирования до семи глав; их материала достаточ¬ но для курса продолжительностью в один семестр. Глава 15, «С++ как улучшенный С», представляет те новые элементы язы¬ ка, которые не являются объектно-ориентированными. Они направлены на со¬ написания обычных, вершенствование процесса процедурно-ориентированных программ. В главе обсуждаются однострочные комментарии, потоковый ввод/вывод, объявления, создание новых типов данных, прототипы функций и проверки типа, встроенные функции (как альтернатива макросам), парамет- ры-ссылки, модификатор const, динамическое распределение памяти, аргу¬ менты по умолчанию, унарная операция разрешения области действия, пере¬ грузка функций, спецификации внешней связи и шаблоны функций. Глава 16, «Классы абстракция данных», дает читателю превосходную абстракцию данных «правильно» при посредстве язы¬ ка (С++), специально предназначенного для реализации абстрактных типов данных (ADT). В последние годы абстракция данных стала главной темой и возможность изучить вводных курсов по программированию, использующих Паскаль. Когда мы пи¬ абстракции данных на базе языка С, но вместо этого решили включить в нее подробное введение в С++. Главы 16, 17 и 18 содержат серьезный материал по абстракции данных. В главе 16 обсуждается реализация ADT как struct и как классов в стиле С++, сали книгу, то рассматривали возможность изложения доступ к элементам класса, отделение ние функций доступа и интерфейса от реализации, использова¬ сервисных функций, инициализация объектов с помо¬ щью конструкторов и их уничтожение с помощью деструкторов, присваива¬ ние путем поэлементного копирования, а также повторное использование про¬ граммного обеспечения. Глава 17, «Классы: часть II» продолжает изучение классов и абстракции обсуждается создание и использование константных объектов, функций-элементов, композиция построение классов, фслю- данных. В ней константных чающих в качестве элементов объекты других классов, дружественные функ¬
26 Как программировать на С ции и классы, имеющие специальные права доступа к закрытым элементам классов, указатель this, позволяющий объекту динамическое распределение памяти, щие единую для всего класса в эволюции результате выразить самую сущность ва или очереди) float С++. Они языка абстрактного и затем создавать классы, итераторы, одно из недавних дополнений, поя¬ позволяют программисту типа данных (например, стека, масси¬ с минимальным количеством дополните¬ версии этого ADT для конкретных типов (таких, как стек int, int и т.д.). По этой причине шаблоны классов часто на¬ льного кода стек свой собственный адрес, информацию, контейнерные и шаблоны классов. Шаблоны классов вившихся знать статические элементы класса, содержа¬ или очередь зывают параметризованными типами. Глава 18, «Перегрузка операций», посвящена одному из наиболее попу¬ лярных разделов в курсах по С++. Студенты изучают ее с удовольствием. Они находят, что материал главы прекрасно соответствует изложению ных типов данных в главах 16 и сообщить компилятору, возможность объектами щие операции с абстракт¬ 17. Перегрузка операций дает программисту как новых типов. следует использовать С++ заранее знает, существую¬ как эти операции применяются к объектам встроенных типов, таких, как целые, числа с плава¬ ющей точкой для него и символы. должен Но допустим, что мы создаем новый класс строк. Что знак «плюс»? Многие программисты используют означать плюс со строками, подразумевая их конкатенацию. В нает, как «перегрузить» данный между двумя в строками ции-операции», которая знак таким выражении, образом, этой главе читатель уз¬ что если он будет компилятор генерирует вызов выполнит конкатенацию этих строк. В стоять «функ¬ главе рассмат¬ операций, ограничения на перегрузку, перегруз¬ ка операций функциями-элементами класса и дружественными функциями, перегрузка одноместных и двуместных операций и операции преобразования одного типа в другой. Особенностью главы является большое число разверну¬ риваются основы перегрузки тых примеров, а именно примеров класса массивов, класса строк, класса даты, (последние два отно¬ упражнениям). Глава 19, «Наследование», имеет дело с одной из фундаментальных черт всех объектно-ориентированных языков. Наследование является формой по¬ класса сверхдлинных целых и класса комплексных чисел сятся к вторного использования программного кода, когда новые классы можно ми новыми быст¬ свойства существующего класса и дополнив их необходимы¬ возможностями. В главе рассматриваются понятия базовых и про¬ ро получить, взяв изводных классов, защищенные элементы, открытое, защищенное и закрытое наследование, непосредственные и косвенные и деструкторов конструкторов наследования в в базовых конструировании и базовые классы, использование производных классах программного и значение обеспечения. Наследование (отношение «является») сравнивается с композицией (отношение «имеет»), а вводятся понятия отношений «использует» и «знает». В главе приво¬ также дится несколько развернутых примеров. В частности, один из них реализует Заключает главу пример слож¬ иерархию классов точек, кругов и цилиндров. ного наследования производный особенности языка класс путем наследования С++, позволяющей сформировать атрибутов и поведения нескольких ба¬ зовых классов. Глава 20, «Виртуальные функции и полиморфизм», посвящена другому фундаментальному аспекту объектно-ориентированного программирования, а именно полиморфному поведению. Когда многие классы связаны друг с другом через наследование одному и тому же базовому классу, объект любого
27 Предисловие из производных классов может рассматриваться как объект базового класса. Это позволяет писать программы в довольно общем виде, не зависящем от конкретного типа объектов производных классов. Новые виды объектов мо¬ обрабатываться той же самой программой, что делает программные сис¬ Полиморфизм позволяет устранить из программ слож¬ ную логику переключателей и сделать код более «прямолинейным». Менед¬ жер экрана игровой программы может, например, просто послать сообщение типа «нарисовать» каждому объекту в связанном списке визуальных объек¬ гут темы расширяемыми. тов. Каждый объект знает, как объект, не модифицируя новый себя нарисовать. В программу можно ввести ее, если новый объект тоже умеет нарисовать разработ¬ интерфейсов пользовате¬ ля. В главе рассматривается механизм реализации полиморфного поведения посредством виртуальных функций. Проводится различение абстрактных классов (которые не могут иметь представителей) и конкретных классов (объекты-представители которых можно создавать). Абстрактные классы по¬ лезны при создании наследуемого интерфейса для всей иерархии классов. В система начис¬ главе разбираются два больших примера на полиморфизм ления заработной платы и вторая версия иерархии точек, кругов и цилинд¬ ров, рассмотренной в главе 19. себя. Такой способ программирования чаще всего используется при ке чрезвычайно популярных сегодня графических Глава 21, «Потоки ввода/вывода ное описание нового, Многие в в С++», содержит чрезвычайно подроб¬ объектно-ориентированном стиле, ввода/вывода С++. курсы по С проводятся с использованием компиляторов С++, и препо¬ предпочитают учить вводу/выводу в новом стиле, не пользуясь старыми функциями printf/scanf. В главе рассматриваются различные аспек¬ ты ввода/вывода С++, куда входит вывод с применением операции помещения даватели часто в поток и ввод с применением операции извлечения из потока, безопасный по ввод/вывод (большое улучшение по сравнению с С), форматируемый и не типу форматируемый (в целях эффективности) ввод/вывод, манипуляторы потока для управления основанием (десятичное, восьмеричное или шестнадцатерич¬ ное основание), представлением чисел с плавающей точкой и шириной поля вывода; определяемые пользователем манипуляторы, состояния ошибки пото¬ ка, ввод/вывод объектов определяемого пользователем привязка типа и вы¬ действительно появля¬ лись на экране перед тем, как пользователь должен дать на них ответ). В нескольких приложениях приводится ценный справочный материал. В частности, в приложении А мы представляем сводку синтаксиса С; сводка всех функций стандартных библиотек С приводится в приложении Б; таблица приоритетов и ассоциации операций дана в приложении В; приложение Г со¬ держит таблицу ASCII-кодов символов; и, наконец, в приложении Д проводит¬ ся обсуждение двоичной, десятичной, восьмеричной и шестнадцатеричной си¬ ходных потоков ко входным (для того, чтобы запросы В основу приложения Б положен, с письменного разрешения института национальных стандартов, документ стандарта ANSI; приложение является детальным, очень ценным для практического стем счисления. Американского программиста материалом. Приложение Д является учебным пособием по сис¬ темам счисления, в которое входят контрольные вопросы и упражнения. Мы в полной мере берем на себя ответственность за недостатки и неточно¬ книге. Мы с благодарностью примем ваши замечания, критику, исправления и предложения по улучшению текста. сти, которые могут встретиться в нашей Пожалуйста, шлите нам свои предложения и дополнения к рубрикам Хоро¬ ший стиль программирования, Распространенные ошибки программирова¬
Как программировать на С 28 эффективности, Советы по переносимости кода замечания. методические Общие Направляйте всю корреспонденцию на наш адрес электронной почты: ния9 Советы по повышению и deitel0world.std.com либо пишите нам: Harvey М. Deitel (author) Paul J. Deitel (author) с/о Computer Science Editor College Book Editorial Prentice Hall Englewood Cliffs, New Jersey 07632 Мы немедленно вам ответим. Харви М. Дейтел Пол Дж. Дейтел
г л в а а i Принципы машинной обработки данных Цели Понять основные принципы организации компьюте¬ ров. Познакомиться с различными типами языков програм¬ мирования. Познакомиться с историей Получить представление языка С. стандартной библиотеке о Понять принципы среды разработки Оценить как языка для обучения возможности С программ С. на С. начального программированию. Оценить преимущества С как базы для дальнейшего изучения программирования вообще и языка С++ в частности.
Глава 1 30 Содержание 1.1. Введение 1.2. Что такое компьютер? 1.3. Внутренняя организация компьютера. 1.4. Пакетная 1.5. Персональные вычисления, распределенные ния 1.6. в обработка, модели мультипрограммирование клиент/сервер ассемблера Машинные языки, языки 1.7. История 1.8. Стандартная библиотека 1.9. Другие языка языки и и разделение времени вычисления языки высокого и вычисле¬ уровня. С высокого С уровня 1.10. Структурное программирование 1.11. Основные принципы среды С 1.12. Общие замечания 1.13. Concurrent 1.14. С о С и об этой книге (Параллельный С) Объектно-ориентированное программирование и С++ стиль программирования Советы по переносимости Советы по повышению эффективности Упражнения для само¬ Ответы к упражнениям для самоконтроля контроля Упражнения Ре¬ комендуемая литература Хороший Резюме программ 1.1. Введение Итак, уважаемый читатель, С! Авторы потратили на языке добро пожаловать в мир программирования на эту книгу немало труда и надеются, что чте¬ ние ее окажется для вас не только полезным, но и занимательным. Поэтому С является который обычно изучают только опытные программисты. книга в ряду учебников по С является уникальной: языком, трудным данная Она подойдет техническим специалистам, которые либо имеют небольшой опыт программирования. Она пригодится кое и строгое и опытным программистам, изложение языка. не которым имеют вообще, требуется глубо¬
Принципы машинной обработки 31 данных Возникает вполне естественный вопрос: каким на удовлетворить потребности образом одна книга способ¬ групп'читателей? Ответ заклю¬ двух различных чается в том, что в книге сделан упор на достижение ясности написания про¬ апробированного метода «структурного программирова¬ Непрограммисты получают возможность изучать программирование граммы посредством ния». «правильным» образом, т.е. начиная с азов. Мы попытались написать книгу в иллюстраций. И что, воз¬ можно, наиболее важно, книга снабжена большим количеством работающих С-программ с образцами выдачи при запуске их на компьютере. Первые четы¬ ре главы вводят фундаментальные принципы процесса вычислений, компью¬ ясной манере со множеством и С. Введение простой и достаточно терного программирования языка в программирование пред¬ структурного подхода. Новички, пользовавшиеся нашим курсом, высказывают мнение, что материал этих глав представляет собой со¬ лидную фундаментальную базу для углубленного изучения С в последующих главах с 5 по 14. Опытные программисты обычно быстро прочитывают первые ставлено в аспекте четыре главы и находят, что изложение материала в главах с 5 по 14 достаточ¬ но строгое и в то же время интересное. Они особенно отмечают детальное опи¬ сание в этих главах указателей, строк, файлов и структур. Многие опытные программисты оценили наше изложение структурному программированию. структурном языке типа Pascal, В основном они а поскольку они никогда ли структурное программирование, то соответственно и был После изучения С по нашей свой стиль программирования. не самым лучшим. можность улучшить лезной интересной информации. Большинство людей так или иначе формально на не изуча¬ написанный ими код книге они получили воз¬ Поэтому здесь он татель новичком, либо опытным программистом, материала по программировали является ли чи¬ найдет много по¬ и знакомы с теми удивительными веща¬ ми, которые могут делать компьютеры. В данном курсе читатель узнает, как Компьютерами, т.е. аппа¬ ратной частью (hardware) управляет программное обеспечение (software), а одним из наиболее популярных на сегодняшний день языков для разработки программного обеспечения является С. Данная книга описывает стандартизо¬ подавать компьютеру команды для их выполнения. ванную версию языка С, принятую в качестве стандарта в 1989 году Амери¬ (American National Standards Instutute ANSI) и Международной организацией стандартов (International Standards Organization ISO). канским институтом национальных стандартов В настоящее время почти во всех отраслях человеческой деятельности более расширяется. На фоне вычислительной техники драматически пользование компьютеров все постоянно щих цен стоимость падает ис¬ расту¬ благодаря значительным успехам как в и в развитии программного обеспечения. занимали не¬ разработке аппаратной части, так Компьютеры, которые 25 лет назад сколько комнат и стоили миллионы долларов, сегодня помещаются в несколь¬ ких кремниевых микросхемах стоимостью по несколько долларов каждая. По иронии судьбы Земле кремний это ингредиент является наиболее распространенным материалом на песка. Технология производства кремние¬ обычного вых микросхем ной, что в настоящее время в мире насчитывается онов сделала компьютеров промышленности, компьютеризацию общего экономически выгод¬ приблизительно 150 милли¬ назначения, которые помогают людям в политике и в число вполне может настолько повседневной быть удвоено. жизни. Через бизнесе, несколько лет это
32 Глава 1 Можно ли изучать С как первый язык программирования, пользуясь, на¬ данной книгой? Мы полагаем, что да. Два года назад, когда в качестве Pascal, мы решилипопробовать. Была написана «Как программировать на С», первое издание текста, который вы сей¬ пример, первого языка люди изучали книга час читаете. Сотни университетов Было доказано, по всему миру использовали эту книгу. курсы на ее основе одинаково эффективны с пред¬ шествующими курсами, ориентированными на Паскаль. Не было замечено значительной разницы за исключением того, что у студентов появилась боль¬ что учебные шая мотивация для изучения языка, потому что они знают, что вероятнее все¬ го в своей профессиональной деятельности им придется программировать на С, Паскале. Студенты, изучающие С, а не на также великолепно понимают, будут более подготовлены для последующего изучения С++. С++ является «надмножеством» языка С, предназначенным для программистов, пишущих что объектно-ориентированные в разделе программы. Более подробно о С++ мы расскажем 1.14. Действительно, сегодня С++ привлекает настолько большое внимание, 15 (и далее) мы даем подробное введение в С++ и объектно-ориен¬ что в главе Интересным явлением на рынке языков про¬ факт, что ведущие производители чаще постав¬ объединенный продукт С/С++, нежели эти языки по отдельности. Это тированное программирование. граммирования является тот ляют дает при возможность пользователям продолжать программирование на необходимости, Итак, перед постепенно на itel@world.std.com. Мы, в свою электронной С, а затем, С++. вами книга по программированию на тель может связаться с нами по тить на каждое перейти С. При желании чита¬ Internet по адресу de- почте через очередь, сделаем все возможное, чтобы отве¬ сообщение. 1.2. Что такое компьютер? это устройство, способное производить вычисления, а так¬ Компьютер же выполнять действия по принятию логических решений со скоростью в миллионы или даже миллиарды раз быстрее, чем человек. Например, боль¬ шинство современных персональных компьютеров могут выполнять десятки арифметических операций в секунду. Человеку с калькулятором в потребуется несколько месяцев, чтобы выполнить такой же объем рабо¬ ты, какой персональный компьютер производит за одну секунду. (Вопрос: как вы узнаете, правильно ли человек складывает цифры? Как вы узнаете, прави¬ льно ли компьютер складывает цифры?) Сегодняшние самые быстрые супер¬ то компьютеры могут выполнять сотни миллиардов сложений в секунду есть такое количество арифметических операций под силу выполнить лишь сотням тысяч человек в течение года! А в исследовательских лабораториях уже работают компьютеры с быстродействием триллион операций в секунду. Компьютеры обрабатывают данные, используя наборы инструкций, назы¬ миллионов руках ваемые компьютерными программами, которые заставляют компьютер вы¬ действия заранее установленном порядке. Действия определяются людьми, которых называют программистами. Различные устройства (такие как клавиатура, экран, диски, память и полнять определенные в микропроцессор), составляющие компьютерную систему, называются аппа¬ ратной частью. Программы, выполняющиеся на компьютере, называются программным обеспечением. Стоимость аппаратуры в последние годы значите¬
Принципы машинной обработки льно упала, ления. благодаря К сожалению, 33 данных чему компьютеры стали доступным предметом обеспечения стоимость программного разрабатывают поскольку программисты При кладные программы. этом в самой все потреб¬ постоянно растет, более мощные и сложные при¬ разработки программного улучшений. В данной книге рассмат¬ технологии обеспечения не наблюдается адекватных риваются методы разработки программ, которые могут существенно сокра¬ тить время и затраты на создание современных программных продуктов высо¬ кого качества. Эти методы включают в себя структурное программирование, пошаговое нисходящее уточнение, разбиение на функции и описанное объектно-ориентированное программирование. в по¬ следнем разделе 1.3. Внутренняя организация компьютера Фактически каждый компьютер, вне зависимости от того, как он выгля¬ дит, можно представить в виде совокупности шести логических 1. Входной блок. Это «принимающая» формацию (данные устройств ввода информация часть компьютера. компьютерные Он получает программы) от ин¬ различных блоки, так что дальнейшем передана на обработку. Часто информации поступает в современные компьютеры че¬ и помещает ее для хранения в другие может основная масса рез и блоков. Это: клавиатуру, быть в напоминающую пишущую машинку. 2. Блок вывода. Он воспринимает обработанную компьютером информа¬ цию и помещает ее на различные устройства вывода так, чтобы сде¬ лать ее доступной для использования вне компьютера. На сегодняшний информация обычно выводится на экран либо распечатывается на бумаге. день 3. Блок памяти. Это хранилище информации, характеризуемое высокой и относительно низкой емкостью. Блок памяти хра¬ скоростью доступа нит информацию, полученную медленно предоставляет ее для от блока ввода, и при обработки. Здесь необходимости не¬ также содержится ин¬ в результате вычислений, до того момента, пока она не будет направлена блоком вывода на устройства вывода. Блок па¬ формация, полученная мяти называют просто памятью, или первичной 4.Арифметико-логическое устройство (АЛУ). часть компьютера. ческих операций, Она памятью. Это «перерабатывающая» несет ответственность за выполнение таких как сложение, арифмети¬ вычитание, умножение и деле¬ ние. В ней также содержатся механизмы принятия решений, которые позволяют компьютеру, например, сравнить два числа из блока памяти и определить, равны они или нет. 5.Центральное процессорное устройство (ЦПУ). Это «административ¬ работы остальных ная» часть компьютера, являющаяся координатором блоков. Устройство центрального процессора направляет команду вход¬ ному устройству поместить информацию в блок памяти, устройству арифметики блоку вода. 2 Зак 801 и вывода логики использовать информацию вычислений, устройство вы¬ для отправить информацию на конкретное
34 Глава 1 6. Блок вторичного компьютера. но другими хранения данных. Программы Это долговременное блоками, обычно помещаются в устройства вторичного хра¬ нения (например, диски), снова, возможно, через час, день, месяц или даже год. 1.4. Пакетная хранилище или данные, которые не используются актив¬ понадобятся до того момента, пока они не обработка, и мультипрограммирование разделение времени Раньше компьютеры могли решать в каждый момент времени только одну Подобная форма работы называется однопользовательской пакетной обработкой. Компьютер обрабатывает данные группами, или пакетами, при¬ чем в каждый момент времени выполняется только одна программа из задачу. группы. В то время пользователи представляли свои программы в вычислите¬ льные центры в виде ной выдачи набора перфокарт и вынуждены часами или даже в течение нескольких были ждать окончатель¬ дней. По мере совершенствования компьютеров становилось обработка редко использует очевидным, что од¬ ресурсы компьюте¬ ра эффективно. Возникла идея, что несколько программ, или задач, могут раз¬ делять между собой ресурсы компьютера, чтобы его загрузка была более ра¬ нопользовательская пакетная циональной. Это называется мультипрограммированием. Мультипрограмми¬ рование подразумевает «одновременное» выполнение на компьютере несколь¬ ких задач; компьютер разделяет свои ресурсы между задачами, на них претен¬ дующими. Но как и прежде, пользователи представляли свои программы на перфокартах и ждали свои результаты по несколько часов. И вот в шестидесятых годах в нескольких промышленных центрах и уни¬ верситетах возникла новаторская концепция это времени разделения ватели получают доступ к компьютеру через терминалы. В типичной компьютерной ходиться несколько сотен но. Процессор выполняет пользователей, решающих свои часть одной задачи, небольшую компьютер возвращается с точки 1.5. к одной и зрения пользователей той когда пользо¬ устройства ввода/вывода, или системе разделения времени может на¬ ся на другую. Это происходит настолько зом, времени. Разделение специальный случай мультипрограммирования, быстро, задачи одновремен¬ затем переключает¬ что в течение одной секунды же задаче несколько раз. их задачи Таким обра¬ решаются одновременно. Персональные вычисления, распределенные и вычисления вычисления в модели клиент/сервер В 1977 году благодаря компании Apple Computer родился феномен персо¬ нальных вычислений. Вначале это было лишь мечтой одержимых программистов-любителей. Компьютеры становились экономически доступными насто¬ лько, чтобы 1981 году люди покупали компания ла на рынке их для решения IBM, мировой лидер своих персональных задач. В по продаже компьютеров, представи¬ Персональный Компьютер IBM. Буквально за одну ночь персона¬ приобрели законное право на свое применение в бизнесе, льные компьютеры промышленности и государственных учреждениях.
Принципы машинной обработки 35 данных были изолированы друг от друга, т.е. были компьютерами-одиночками. Люди выполняли работу на своих собственных машинах, а затем передавали друг другу диски для обмена информацией. Не¬ Однако смотря эти компьютеры на то, что ранние персональные компьютеры были не настолько мощ¬ чтобы обеспечивать разделение времени для нескольких пользовате¬ эти машины могли быть объединены в компьютерные сети посредством лей, ными, в локальные сети в пределах одной организации. Это привело к появлению феномена распределенных вычислений, когда работа всей организации, вместо того, чтобы быть выполненной непосредственно на телефонных линий или центральном компьютере, распределялась по сети на рабочие места, на кото¬ Персональные компьютеры были достаточ¬ но мощными, чтобы удовлетворить требования индивидуальных пользовате¬ лей, а также справляться с задачами по электронному обмену информацией. рых и производились все расчеты. Современные персональные компьютеры обладают такой же вычислитель¬ ной мощностью, как и машины стоимостью в миллион долларов десятилетней обеспечивают ин¬ давности. Наиболее мощные машины рабочие станции дивидуальных пользователей невиданными возможностями. Информация легко распределяется по компьютерным сетям, в которых отдельные компью¬ теры, серверами, хранят общий для называемые всех набор ных, которые могут быть использованы компьютерами щими в состав сети. С++ стали программ и дан¬ клиентами, входя¬ Отсюда происходит термин клиент/сервер. Языки С и теми языками программирования, на которых пишут программное обеспечение для операционных систем, компьютерных сетей ных программ модели и для приклад¬ клиент/сервер. 1.6. Машинные языки, языки высокого ассемблера и языки уровня Программисты пишут программы на различных языках программирова¬ ния, некоторые из которых непосредственно понятны компьютеру, а другие нуждаются в промежуточной стадии трансляции. Сотни имеющихся языков могут быть подразделены на три 1. Машинные языки высокого Каждый компьютер рый типа: языки 2. Ассемблерные 3. Языки общих уровня может понимать только свой машинный язык, кото¬ Он тесно связан является естественным языком конкретного компьютера. с его аппаратной частью. Машинные языки в общем случае состоят из после¬ довательностей чисел (обычно нулей и единиц), которые являются командами операций. Машинные языки явля¬ конкретный машинный язык может быть ис¬ на выполнение одиночных элементарных ются машинно-зависимыми, т.е. пользован только с определенным типом компьютера. добны Машинные языки неу¬ для восприятия человеком, что можно проиллюстрировать небольшой программой, в которой к тарифной ставке прибавляются выплаты за сверх¬ урочную работу, а результат сохраняется в переменной общей выплаты. 2*
36 Глава 1 + 1300042774 + 1400593419 + 1200274027 По мере распространения компьютеров становилось очевидным, граммирование на машинных языках тормозит развитие является ники, непосильным очень тех¬ большинства программистов Вместо последовательностей чисел, непосредственно медленным занятием. понятных компьютеру, и что про¬ компьютерной для программисты для представления элементарных опе¬ аббревиатуры, которые и сформирова¬ ли основу языков ассемблера. Для преобразования программ, написанных на таких языках, в машинный язык были разработаны программы-транслято¬ раций стали применять англоязычные называемые ассемблерами. Преобразование происходило со скоростью, равной быстродействию компьютера. Нижеприведенный фрагмент программы на языке ассемблера также вычисляет основную стоимость в виде суммы та¬ рифной ставки и сверхурочных. При этом программа становится более понят¬ ры, ной, чем аналогичная на машинном языке: LOAD BASEPAY ADD OVERPAY STORE GROSSPAY С ассемблера использование компьютеров значитель¬ требовалось написание большого количества инструкций даже для реализации решения простейших задач. Для ускорения процесса программирования были разработаны языки высокого уровня, в ко¬ торых для выполнения сложных действий достаточно написать один опера¬ тор. Программы для преобразования последовательности операторов на языке появлением языков но расширилось, однако все еще высокого уровня в машинный язык называются компиляторами. В языках высокого уровня инструкции, написанные программистами, зачастую выгля¬ дят как обычный текст на английском языке с применением общепринятых Уже математических знаков. знакомое нам вычисление суммарной выплаты выглядит так: grossPay = + basePay overPay Совершенно очевидно, что с уровня более предпочтительны, точки зрения программистов языки высокого чем любые машинные или ассемблерные язы¬ ки. 1.7. История языка С Язык С берет свое начало от двух языков, BCPL и В. В 1967 году Мартин Ричардс разработал BCPL как язык для написания системного программного обеспечения и компиляторов. В 1970 году Кен Томпсон использовал В для со¬ здания версий операционной системы UNIX на компьютере DEC BCPL, так и в В переменные не разделялись на типы каждое ранних PDP-7. Как в значение данных занимало одно слово в памяти и ответственность за различе¬ ние, например, целых и действительных чисел целиком ложилась на плечи программиста. Язык С был разработан (на основе В) Деннисом Ричи из Bell Laboratories и был реализован в 1972 году на компьютере DEC PDP-11. Известность впервые С получил в качестве языка операционной системы UNIX. Сегодня практиче¬ С и/или С++. По проше¬ ски все основные операционные системы написаны на
Принципы машинной обработки ствии двух Язык С десятилетий С не зависит от 37 данных имеется в наличии на аппаратной большинстве компьютеров. части, и программы, написанные на нем, мо¬ гут быть перенесены на многие системы. С сочетает в себе основные принципы языков BCPL и В; кроме того, в нем введена типизация переменных и некото¬ рые другие важные моменты. В конце 70-х годов С превратился в то, что мы называем С». Публикация в 1978 году С» привлекла широкое ния мых удачных среди изданий книги Кернигана и внимание к языку. «традиционный Ричи «Язык программирова¬ Эта книга стала одной из са¬ по компьютерным дисциплинам, выпущенных в свет. Применение С для разных типов компьютеров ратными платформами) привело которые, несмотря на свою (иногда называемых аппа¬ к появлению различных вариантов языка, схожесть, были зачастую несовместимыми. Это серьезной проблемой для разработчиков программных продуктов, ко¬ торым требовалось разрабатывать коды, способные работать на нескольких платформах. Становилось ясно, что нужна стандартная версия С. В 1983 году Американским комитетом национальных стандартов в области компьютеров и обработке информации (X3) был учрежден технический комитет X3J11, перед которым ставилось целью выработать «однозначное и машинно-независимое определение языка С». В 1989 году стандарт был одобрен. Документ известен как ANSI/ISO 9899:1990. Копии могут быть заказаны через Американский явилось институт национальных стандартов, Второе издание книги эту версию Кернигана и С, называемую ANSI С адрес которого указан в предисловии. Ричи выпущено в 1988 году и широко используемую в и описывает мире (Ke88). Совет по переносимости программ 1.1 что С является машинно-независимым и широко доступным языком, прикладные программы, написанные на С, могут работать практически без измене¬ ний на большинстве компьютерных систем. Благодаря тому, 1.8. Как лей, сать Стандартная библиотека С 5, программы, написанные на С, состоят из моду¬ функциями. Пользователь сам может напи¬ создания программы функции, однако большинство вы узнаете в главе фрагментов, необходимые для называемых или программистов предпочитают применять уже существующие, которые нахо¬ стандартной библиотеке С. Таким образом, есть два аспекта изуче¬ умение пользо¬ Первый изучение непосредственно С и второй ваться стандартной библиотекой. В данной книге рассматриваются многие функции из стандартной библиотеки, все они приводятся в приложении Б дятся в ния языка. (так, как их граммистам, определяет желающим ANSI С). Книга Плогера (P192) будет приобрести глубокие знания и полезна про¬ понимание работы стандартных функций, их применения при написании компактных кодов. В данном курсе читателю будет предложен блочный метод построения программы. Не следует изобретать велосипед. Применение готовых частей на¬ зывается повторным использованием кода это ключевой момент в развива¬ ющейся сейчас области объектно-ориентированного программирования (гла¬ ва 15). В языке С при формировании программы вы будете использовать сле¬ дующие «строительные блоки»:
38 Глава 1 Функции стандартной библиотеки С Функции, которые вы создадите сами Функции, написанные Преимущество другими людьми создания своих собственных отлично знаете и понимаете, как они кода С. функций состоит работают. У вас появится работой Ну потребуются для создания новых функций. При использовании существующих функций проследить за а недостаток в том, что вы возможность временные затраты, которые не приходится изобретать ве¬ В случае применения стандартных функций ANSI, которые, как вы очень тщательно написаны, ваши программы с большей вероятностью лосипед. знаете, могут быть перенесены на другие типы компьютеров. Совет по повышению эффективности 1.1 Использование функций стандартной библиотеки ANSI вместо написания собствен¬ ных аналогичных функций может улучшить характеристики программы, т к стандар¬ тные функции написаны весьма тщательно и работают очень эффективно Совет по переносимости программ 1.2 Использование функций стандартной библиотеки ANSI вместо написания собствен¬ ных аналогичных функций может способствовать переносимости программ, т к стан¬ дартные функции реализованы практически во всех существующих вариантах С 1.9. Несмотря лько Другие на то, что некоторые из языки высокого были разработаны них нашли широкое уровня сотни языков высокого уровня, то¬ применение. TRANslator) разработан фирмой IBM между 1954 вания в научных и инженерных программах, и FORTRAN (FORmula 1957 годами для требующих использо¬ сложных математи¬ вычислений. FORTRAN широко применяется и до сих пор. COBOL (COmmon Business Oriented Language) разработан в 1959 году груп¬ пой, в которую входили производители и пользователи компьютеров. COBOL ческих главным образом используется в коммерческих прикладных программах, ког¬ да необходима высокая точность при обработке большого количества данных. Сегодня больше половины программного использования в бизнесе, человек в настоящее время Pascal был создан лее зарабатывают 1.10. COBOL. Свыше для миллиона на жизнь, программируя на COBOL. С. Он предназначен для ака¬ остановимся на языке Pascal бо¬ почти в то же время, что и целей. В следующем разделе подробно. демических обеспечения, предназначенного все еще написано на языке мы Структурное программирование В шестидесятых годах при разработке программного обеспечения про¬ граммисты испытывали серьезные затруднения. Время разработки обычно за¬ тягивалось свыше установленных сроков, стоимость шала бюджет, а окончательный продукт был работ значительно превы¬ весьма ненадежен. Люди начали
Принципы машинной обработки 39 данных разработка программного продукта является делом более слож¬ ным, чем они себе представляли. В результате научных исследований, прове¬ денных в шестидесятых годах, возникла концепция структурного програм¬ мирования> т.е. такого подхода к написанию программ, при котором они ста¬ понимать, что новятся более ясными для понимания, более корректными и лучше приспо¬ собленными для модификации в дальнейшем. В главах 3 и 4 приводится обзор принципов структурного программирования. Ниже мы остановимся лишь на развития структурного программирования на С. Одним из знаменательных результатов научных исследований в области про¬ граммирования явилось создание в 1971 году профессором Никласом Виртом обсуждении языка программирования Pascal. Pascal, названный по имени математика и фи¬ семнадцатого века Блеза Паскаля, был разработан для обучения струк¬ турному программированию в академических кругах и быстро распространился в университетах для изучения в качестве первого языка программирования. К лософа сожалению, в нем отсутствуют свойства, благодаря которым его можно было бы использовать в коммерческой и индустриальной сферах деятельности и, как следствие, в этих областях он не получил широкого распространения. Однако можно отметить, что реальное значение этого языка в том, что он лег в основу Ada. Разработка языка Ada финансировалась Мини¬ начале 80-х годов. Для обслуживания стерством обороны США в конце 70-х огромного комплекса МО использовались сотни различных языков. Для их заме¬ ны было принято решение создать один язык программирования, который бы языка программирования удовлетворял всем требованиям. Для этой цели был выбран Pascal, однако конеч¬ ный продукт Ada значительно от него отличается. Новый язык программи¬ был назван по имени леди Ады Лавлейс, дочери поэта лорда Байрона. Ада Лавлейс в начале 1800-х написала первую в мире компьютер¬ ную программу. Одной из основных черт языка Ada является многозадачность, которая позволяет нескольким процессам протекать параллельно. Остальные языки высокого уровня, которые мы обсуждали, включая С и С++, допускают выполнение в данный момент только одной задачи. рования Считается, что 1.11. Все С-системы Основные принципы среды С общем состоят из трех частей: среды программирования, стандартной библиотеки С. В процессе подготовки С-программы проходят через шесть стадий (рис. 1.1). Это: редактирование, npenpoцессорная обработка, компиляция, компоновка, загрузка и исполнение. Здесь мы имеем в виду типичную UNIX-систему С. В случае если читатель использу¬ ет другую операционную систему, ему следует обратиться к соответствующему собственно в языка и справочному руководству или к более опытному коллеге за разъяснениями, как эти задачи выполняются в его среде. Первая фаза состоит из редактирования файла при помощи программы-рев которой программист набирает программу и производит необходи¬ дактора, мые исправления. Затем программа сохраняется на вторичном запоминающем например, диске. Имена файлов, содержащих программы на С, устройстве, должны иметь расширение .c. В системах UNIX широко используются два ре¬ дактора vi и emacs. Пакеты С/С++, такие, как Borland С++ для IBM и со¬ компьютеров или Symantec С++ для Apple Macin¬ вместимых персональных tosh, обладают встроенными редакторами, интегрированными в среду про¬ граммирования. Мы предполагаем, что читатель знает, как отредактировать программу.
Глава 1 40 Этап 1: Программа создается редактором Редактор и запоминается на диске Этап 2: Этап 3: Программа предварительной обработки преобразовывает Препроцессор Компилятор создает объектный код и сохраняет его на диске Компилятор Диск Этап 4: код Компоновщик связывает объектный код с библиотеками, создает a.out и сохраняет его на диске Компоновщик Первичная память Этап 5: Загрузчик Загрузчик размещает программу в памяти Диск Первичная память ЦПУ Этап 6: ЦПУ выбирает каждую инструкцию и выполняет ее, возможно сохраняя новые значения данных по мере выполнения программы Рис. 1.1. Типичная Далее, среда С программист дает команду на компиляцию программы. Компиля¬ С-программу в код кодом). В С-системе (который также называ¬ тор транслирует машинного языка ют объектным перед началом фазы трансляции автомати¬ чески выполняется программа препроцессора. Препроцессор С подчиняется
Принципы машинной обработки 41 данных директивам препроцессора, которые определяют, какие действия должны быть выполнены перед компиляцией программы. Обычно это добавление других файлов к компилируемому и замена специаль¬ специальным командам ных символов в тексте программы. описываются в 13. Препроцессор глава священа В общих чертах директивы препроцессора начальных главах книги, перед тем, как начнется а детальному их рассмотрению по¬ автоматически преобразование запускается программы в компилятором машинный язык. Четвертая фаза называется компоновкой. Типичные С-программы содер¬ жат обращения к функциям, чьи определения находятся либо в стандартных библиотеках, либо в библиотеках, созданных группой пользователей в процес¬ се работы над конкретным проектом. То есть объектный код, получившийся в результате работы компилятора, содержит «дыры», обусловленные отсутстви¬ ем определений функций, к которым происходит обращение. Компоновщик связывает объектный код с определениями отсутствующих функций, т.е. вы¬ рабатывает исполняемый образ (в котором уже нет отсутствующих частей). Например, для компиляции и компоновки программы с именем welcome.c следует после подсказки UNIX напечатать cc welcome.c и нажать вится клавишу Enter. В случае корректной компиляции и компоновки поя¬ являющимся исполняемым кодом программы welcome.c. файл a.out, Пятая фаза называется быть размещена загрузкой. Перед выполнением программа должна Эту задачу выполняет загрузчик, который помеща¬ в памяти. ет в память исполняемый код с диска. И наконец, компьютер, под контролем ЦПУ, следовательно прочитывая ее инструкции одну за полнения программы в системе UNIX выполняет программу, другой. Для по¬ загрузки и вы¬ после приглашения мы печатаем a.out и Enter. нажимаем Большинство С-программ получают и (или) выводят данные. Конкретные С от stdin (standard input device стан¬ получают входные данные функции дартное устройство ввода), которое, как правило, ассоциировано с клавиату¬ рой; однако, stdin может быть подключено и к другому устройству. Вывод данных осуществляется на stdout (standard output device стандартное устройство вывода), которым обычно является экран компьютера, однако так¬ же может быть подключено к другому устройству. Когда мы говорим, что про¬ грамма печатает результат, на экране. или Данные принтер. мы подразумеваем, что результат высвечивается могут выводиться на другие Имеется также stderr (standard устройства, error устройство ошибок). Устройство stderr (обычно такие как диск device стандартное ассоциированное с экраном) используется для вывода сообщений об ошибках. Часто для вывода данных, т.е. для stdout, вместо экрана применяют другие устройства, а экран исполь¬ зуют для вывода сообщений об ошибках, т.е. в качестве stderr, чтобы пользо¬ быстрее был информирован о возникающих ошибках. ватель как можно 1.12. С это Общие трудный язык. замечания о С и этой книге Опытные программисты зачастую гордятся своим умением писать запутанные и загадочные тексты программ. Однако плохой становится неудо¬ стиль программирования. В результате программа бочитаемой, увеличивается вероятность сбоев и затрудняется ее это просто отладка и
Глава 1 42 проверка. Данная книга предназначена для начинающих программистов, поэ¬ тому мы делаем упор на написание ясных, грамм. Одной из ключевых написании при целей этой программы через структурного программирования и хорошо структурированных про¬ книги является достижение четкости применение благодаря апробированной методики связанному с ней хорошему сти¬ лю программирования. Хороший стиль программирования 1.1 Пишите программы в («keep it simple» Возможно, простой, четкой манере Подобный стиль иногда называют KIS будьте проще) - читатель слышал, что ные на нем программы могут работать С переносимый язык и что написан¬ на разных компьютерах. носимость является весьма труднодостижимым Однако пере¬ свойством. Стандартный доку¬ ANSI насчитывает 11 страниц, посвященным проблеме переносимости. На тему переносимости С написаны целые книги (Ja89), (Ra90). мент Совет по переносимости программ 1.3 Несмотря на возможность написания переносимых программ существует много проб¬ лем совместимости между различными версиями С и разными компьютерными сис¬ темами, грамм что на С затрудняет достижение не обеспечивает их переносимости Само по себе написание про¬ переносимости Мы очень тщательно проработали стандартный документ ANSI С и в соот¬ очень богатый язык и ветствии с ним подготовили нашу книгу. Однако С некоторые его тонкости в книге не рассматриваются. бится дополнительное техническое описание ANSI С, Если читателю понадо¬ то мы рекомендуем ему ANSI С или книгу Кернигана и Ричи (Ke88). Мы обсуждение ANSI С, который по многим свойствам несо¬ самому прочитать стандарт ограничиваем наше вместим с ранними версиями языка, и в которые программы, Хороший стиль не работающие этой книге читатель может со старыми найти не¬ С-компиляторами. программирования 1.2 Читайте руководства пользователя по версиям С, с которыми вы работаете Частое обращение к ним позволит вам узнать много полезного об особенностях языка С и поможет их корректно использовать Хороший стиль программирования 1.3 Хорошими учителями являются компьютер и компилятор. Если вы не уверены в том, как работает та или иная конструкция С, то следует написать пробную программу, от¬ компилировать, запустить ее и посмотреть, 1.13. Concurrent С что получится (Параллельный С) В результате продолжающейся исследовательской работы в Bell Laborato¬ ries были разработаны другие версии С. Гехани (Ge89) разработал Concurrent С надмножество, обладающее возможностями для парал¬ (Параллельный С)
Принципы машинной обработки 43 данных лельного запуска нескольких задач. Языки типа Concurrent ционные системы, поддерживающие параллельную работу, С, а также опера¬ станут более попу¬ лярны в следующем десятилетии с ростом использования мультипроцессоров (т.е. компьютеров с несколькими ЦПУ). В настоящее время Concurrent С не более чем исследовательский язык. Книги по операционным системам (De90), как правило, уделяют значительное внимание параллельному программирова¬ нию. 1.14. Объектно-ориентированное и программирование С++ В лаборатории Bell Бьерном Страуструпом (St86) было разработано еще одно надмножество С, а именно С++. Язык С++ имеет целый ряд качеств, «украшающих» обычный С. Но чцля самое главное, он предоставляет возможности объектно-ориентированного программирования. Объекты это в принципе многократно используемые компоненты про¬ обеспечения, которые моделируют реальные сущности. В мире про¬ граммных продуктов произошла революция. Быстрое, корректное и экономи¬ граммного чески выгодное построение программного нодостижимой целью, вом, мощном обеспечения все еще остается труд¬ а время таково, что воздух пронизан потребностью В настоящее время разработчики сознают, что применение ентированного подхода способно повысить производительность лективов в в но¬ программном обеспечении. 10-100 раз по сравнению со объектно-орирабочих кол¬ стандартной методикой программиро¬ вания. ет Разработано много объектно-ориентированных языков. Однако существу¬ убежденность, что именно С++ станет доминантным системным языком се¬ конца 90-х годов. редины людей Большинство гией обучения главы с 15 мирование склоняется к тому, что сегодня наилучшей страте¬ С++. Поэтому 21 посвящены введению в объектно-ориентированное програм¬ С++. Мы надеемся, что они будут оценены читателем, и что после является первоначальное изучение С, а затем по и прочтения книги он продолжит изучение С++. Резюме Аппаратной обеспечение ANSI-C компьютера (hardware) управляет это версия языка программирования стандарта в дартов частью программное (software). С, принятая в качестве 1989 году Американским институтом национальных (ANSI) и Международной организацией стандартов стан¬ (ISO). которые 25 лет назад занимали несколько комнат и стои¬ ли миллионы долларов, сегодня помещаются в нескольких кремниевых микросхем стоимостью по несколько долларов каждая. Компьютеры, В настоящее время в мире насчитывается приблизительно 150 миллио¬ назначения, которые помогают людям в биз¬ несе, промышленности, политике и в повседневной жизни. Через не¬ нов компьютеров сколько лет общего это'число может быть удвоено.
Глава 1 44 Компьютер устройство, способное производить вычисления, а действия по принятию логических решений со скоро¬ это также выполнять в стью миллионы или даже Компьютеры обрабатыБают данные чем раз быстрее, миллиарды под управлением человек. компьютерных программ. Различные устройства (такие, как клавиатура, экран, диски, память и устройства), составляющие аппаратной частью. процессорные зываются ее компьютерную систему, на¬ Компьютерные программы, выполняющиеся ся программным обеспечением. Входной блок информации это Блок вывода ции. На «принимающая» в современные или компьютере, называют¬ часть компьютера. это часть компьютера, отвечающая за выдачу сегодняшний памяти это первичной день большинство информации информа¬ выводится на эк¬ бумаге. «хранилище» компьютера, памятью, именуемое памятью. Арифметико-логическое устройство (АЛУ) выполняет ветственно за процесс принятия решений. Центральное Большинство компьютеры поступает через клавиатуру. ран либо распечатывается на Блок в процессорное устройство (ЦПУ) это вычисления и от¬ «административ¬ работы ная» часть компьютера, являющаяся координатором остальных блоков. Программы или данные, которые не активно используются другими блоками, обычно помещаются на устройства вторичного хранения (на¬ пример, диски), пока они не понадобятся снова. При однозадачной пакетной обработке ми, или времени пакетами, причем одну-единственную данные обрабатываются компьютер выполняет в каждый группа¬ момент программу. подразумевает одновременное выполнение на компьютере нескольких задач компьютер распределяет свои ре¬ ними. сурсы между Мультипрограммирование Разделение времени специальный случай мультипрограммирова¬ это ния, когда пользователи получают доступ к компьютеру через ства ввода/вывода, задачи При решаются или терминалы. точки зрения устрой¬ пользователей их одновременно. распределенных вычислениях ляется по сети на С рабочие работа всей организации распреде¬ места, на которых и производятся все расче¬ ты. хранят общий для всех пользователей набор программ и дан¬ клиентами, ных, которые могут быть использованы компьютерами Серверы входящими в состав сети. Отсюда происходит термин «модель кли¬ ент/сервер». Каждый компьютер шинный язык. может непосредственно понимать только свой ма¬
Принципы машинной обработки Машинные сел (в языки в 45 данных общем случае состоят из последовательностей чи¬ единицам), которые явля¬ конечном счете сводящихся к нулям и ются командами на выполнение одиночных элементарных операций. Машинные языки являются машинно-зависимыми. Основу языков ассемблера образуют англоязычные аббревиатуры. Ас¬ семблеры транслируют программы, написанные на ассемблерных язы¬ машинный в ках, язык. для преобразования последовательности операторов на языке высокого уровня в машинный язык называются компиляторами. Программы С известен как Программы, язык разработки операционной написанные на С, могут быть перенесены одобрен на UNIX. большинство систем. существующих компьютерных В 1989 году был системы стандарт ANSI С. FORTRAN (FORmula TRANslator) используется в научных и инженер¬ ных программах. COBOL (COmmon Business Oriented Language) главным зуется в коммерческих прикладных программах, когда сокая точность Структурное грамм, лее при обработке большого программирование и лучше данных. это такой подход к написанию про¬ при котором они становятся корректными количества образом исполь¬ необходима вы¬ более подходящими ясными для понимания, для бо¬ дальнейших модифика¬ ций. Pascal был разработан для обучения структурному программированию в кругах. академических Ada финансировалась Министерством обороны США. для его создания был выбран Pascal. Разработка языка В основы качестве Одной из основных черт языка позволяет нескольким Ada процессам является многозадачность, которая протекать параллельно. общем состоят из трех частей: среды, языка програм¬ мирования и стандартной библиотеки С. Библиотечные функции не яв¬ ляются собственно частью языка С; они выполняют такие операции, как ввод/вывод данных, а также математические вычисления. Все С-системы в В процессе подготовки С-программы проходят через шесть стадий: ре¬ обработка, компиляция, компоновка, дактирование, препроцессорная загрузка и выполнение. Программист набирает программу и производит необходимые исправ¬ ления при помощи программы-редактора. Компилятор транслирует программу на С в торый также называют объектным кодом). Препроцессор С (ко¬ специальным командам директивам обычно указывают, что к компилируемому файлу следует добавить другие файлы и должна быть произведена за¬ мена специальных символов в тексте программы. препроцессора, подчиняется код машинного языка которые
46 Глава 1 Компоновщик щих функций, связывает объектный код с определениями отсутствую¬ вырабатывает частей). т.е. отсутствующих нет помещает в память исполняемый код с диска. Загрузчик Компьютер, льно исполняемый код (в котором уже под контролем ее прочитывая ЦПУ, инструкции выполняет одну за программу, последовтге- другой. Некоторые функции С (например, scanf) получают входные данные от stdin (стандартного устройства ввода), которое, как правило, ассоции¬ ровано с клавиатурой. Вывод да), данных осуществляется на stdout (стандартное устройство выво¬ которым обычно является экран компьютера. Имеется также stderr (стандартное устройство ошибок). Устройство stderr (обычно ассоциированное с экраном) используется для вывода сробщений об ошибках. Несмотря вует на возможность написания переносимых много проблем совместимости между программ, различными сущест¬ версиями С и разными компьютерными системами, что затрудняет достижение пере¬ носимости. Concurrent С это параллельного надмножество запуска С++ предоставляет нескольких возможности С, обладающее возможностями для задач. для объектно-ориентированного про¬ граммирования. Объекты это программного в принципе многократно используемые компоненты обеспечения, которые моделируют реальные сущности. Существует общепринятое мнение, что именно С++ станет ведущим си¬ стемным языком середины конца 90-х годов. Терминология С запуск программы с++ исполнение программы COBOL исполняемый Concurrent С клиент CPU компилятор FORTRAN компоновщик Pascal компьютер UNIX компьютерная аппаратная платформа логические аппаратная блок часть метод программа блоки машинно-зависимый машинно-независимый памяти блочный образ построения программ ввод/вывод (I/O) входной блок выходной блок машинный язык многозадачность данные клиент/сервер мультипрограммирование мультипроцессор нисходящее пошаговое уточнение естественный язык компьютера объект загрузчик объектно ориентированное задача модель программирование
Принципы машинной обработки 47 данных объектный код стандартная библиотека С стандартная ошибка (stderr) окружение (рабочая среда) память первичная память стандартный ввод (stdin) стандартный вывод (stdout) переносимость структурное программирование персональный компьютер повторное использование терминал программного кода препроцессор С устройство вывода программа-транслятор файловый сервер программист функция программное обеспечение центральное процессорное суперкомпьютер устройство ввода рабочая станция разбиение на функции устройство (CPU) разделение времени распределенные вычисления экран язык высокого уровня язык программирования редактор ясность сохранение программы Хороший 1.1. простой, четкой манере. Подобный будьте проще). в стиль ино¬ KIS («keep it simple» называют Читайте руководства пользователя по версиям С, с которыми вы ра¬ ботаете. Частое обращение к ним позволит вам узнать много полезно¬ го 1.3. программирования Пишите программы гда 1.2. стиль об особенностях языка Хорошими учителями ' С и поможет их корректно использовать. являются компьютер и компилятор. Если вы работает та или иная конструкция С, то следу¬ пробную программу, откомпилировать, запустить ее и не уверены в том, как ет написать посмотреть, что получится. Советы по переносимости программ 1.1. тому, что С является машинно-независимым и широко доступным языком, прикладные программы, написанные на С, мо¬ гут работать практически без изменений на большинстве компью¬ Благодаря терных 1.2. систем. Использование функций стандартной библиотеки ANSI вместо напи¬ сания собственных аналогичных функций может улучшить перено¬ симость программы, т.к. чески 1.3. во Несмотря всех проблем вает С. совместимости между различными версиями разными компьютерными системами, переносимости. Само по себе С что затрудняет достижение написание программ на С не обеспечи¬ переносимости. Советы по повышению 1.1. функции реализованы факти¬ вариантах на возможность написания переносимых программ, суще¬ ствует много и стандартные существующих эффективносги Использование функций стандартной библиотеки ANSI вместо напи¬ сания собственных аналогичных функций может улучшить характе¬ ристики программы, тщательно и т.к. работают функции эффективно. стандартные очень написаны весьма
48 Глава 1 Упражнения 1.1. для самоконтроля Заполните пробелы в каждом а) Компания, которая ний, из следующих феномен персональных ввела в мир называется предложений: вычисле¬ . которому персональные вычисления были промышленности и бизнеса, называется б) Компьютер, благодаря введены в практику в) Компьютеры обрабатывают называемым г) Шестью данные, следуя компьютерными ключевыми логическими д) это наборам инструкций, . блоками компьютера являются: специальный случай мультипрограммирова¬ ния, при котором пользователи получают доступ к компьютеру по¬ средством е) В главе под названием устройств обсуждаются «терминалы». три класса языков: и , которые транслируют код с языков высокого уровня на машинный язык, называются ж) Программы, . з) С широко известен как язык, на котором написана операционная система . и) Книга и С рассматривает версию С, которая называется была стандартизирована Американским национальным институ¬ том стандартов. к) Язык был разработан Виртом ному программированию в обучения для структур¬ университетах. л) В Министерстве обороны был разработан язык Ada, который ха¬ позволяющей программистам запускать рактеризуется , несколько 1.2. задач параллельно. Заполните пробелы в предложениях о среде С. следующих а) С-программы обычно набирают грамму б) на компьютере, используя про¬ . В С-системе перед началом фазы трансляции автоматически полняется программа в) Двумя наиболее частыми обработки являются г) Программа объединяет выходные течными функциями. д) Программа вы¬ . операциями во время и для препроцессорной . формирования исполняемого кода данные компилятора с различными библио¬ перемещает исполняемый код с диска в память. е) Для того, чтобы загрузить и выполнить только что откомпилиро¬ ванную программу в системе UNIX, следует ввести .
Принципы машинной обработки 49 данных Ответы к упражнениям для самоконтроля 1.1. а) Apple, б) IBM PC, в) программами, г) блок ввода, блок вывода, арифметико-логическое устройство (АЛУ), централь¬ ное процессорное устройство, блок вторичного хранения данных, д) разделение времени, e) машинные языки, языки ассемблера и язы¬ ки высокого уровня, ж) компиляторами, з) UNIX, и) ANSI, к) Pas¬ cal, л) многозадачностью. блок памяти, 1.2. а) редактора, б) препроцессора, в) включение в компилируемый файл других файлов, замена специальных символов в тексте про¬ граммы, г) компоновщика, д) загрузчика, e) a.out. Упражнения 1.3. Ответьте, к какой группе относятся обеспечению аппаратной следующие или части программному объекты: а) ЦПУ б) Компилятор С в) АЛУ г) Препроцессор С д) Блок ввода текстового е) Программа 1.4. Для чего вам может редактора понадобиться зависимом языке вместо того, писать программу на машинно-не- чтобы писать ее на машинно-зависи¬ мом? Почему машинно-зависимый язык ходящим для написания определенных может оказаться типов более под¬ программ? 1.5. Программы-трансляторы, такие как ассемблеры и компиляторы, преобразуют программы с одного языка (называемого исходным) на другой (называемый объектным языком). Определите, какие из сле¬ дующих утверждений справедливы, а какие нет: а) Компилятор кого уровня, б) Ассемблер ный транслирует программы, написанные на языке высо¬ в объектный язык. транслирует программы с исходного языка на машин¬ язык. в) Компилятор преобразует граммы на г) Языки объектном высокого программы на исходном языке в про¬ языке. уровня, как правило, машинно-зависимы. запуском программы на компьютере программа, написан¬ ная на машинном языке, нуждается в трансляции. д) Перед 1.6. Заполните пробелы в каждом из следующих предложений: а) Устройства, посредством которых пользователи получают доступ к системам с разделением времени, называются . б) Компьютерная ных программ в программа, которая преобразует коды ассемблер¬ программы на машинном языке, называется
Глава 1 50 в) Логический блок компьютера, который получает информацию из¬ вне для использования внутри компьютера, называется . г) Процесс д) Язык какого типа . англоязычные использует команд на машинном языке? е) Какие шесть имеются быть з) Общим написанные язык, вне названием на программ, конкретном компьютера? обработанную информация может так что . которые преобразуют компьютерном в языке, программы, машинный . и) Какой логический блок к) Какой для посылает уже устройства, компьютера? является блоков логических на различные использована аббревиатуры . ж) Какой логический блок компьютера информацию для решения конк¬ инструкций компьютеру написания ретных задач называется логический блок компьютера компьютера хранит информацию? выполняет л) Какой логический блок компьютера принимает ния? вычисления? логические реше¬ . м) Какая общепринятая аббревиатура управляющего блока н) Какой сту для используется для обозначения компьютера? . уровень компьютерного языка наиболее быстрого и легкого написания о) Общепризнанный ориентированный это программирования п) Единственный Определите, язык компьютера координирует деятельность . какие из следующих нет. а) Машинные б) При бизнес современный . всех остальных блоков? какие . компьютера. p) Какой логический блок а на программи¬ язык, непосредственно понятный компьютеру, на¬ зывается 1.7. удобен программ? Объясните языки, свои утверждений являются верными, ответы: как правило, машинно-зависимы. разделении времени несколько пользователей действительно одновременно используют компьютер. в) Как и другие языки высокого уровня, шинно-независимый 1.8. Обсудите значение С рассматривается как ма¬ язык. каждого из следующих имен в среде UNIX: а) stdin б) stdout в) stderr 1.9. Какой ключевой особенностью обладает Concurrent С, которой ANSI С? нет в
Принципы машинной обработки 1.10.Почему 51 данных сегодня уделяется большое внимание вообще ванному программированию и языку объектно-ориентироС++ в частности? Рекомендуемая литература (An90) ANSI, American National Standard for Information Systems-Programming Language C (ANSI Document ANSI/ISO 9899: 1990), New York. NY: American National Standards Institute. 1990. no ANSI С. Распространение документа осу¬ ществляется Американским институтом национальных стандар¬ тов, 1430 Broadway, New York 10018. Это первоисточник (De90) Deitel, H. М., Operating Systems (Second Edition), Reading. MA: Addison-Wesley Publishing Company, 1990. Книга тем. о традиционном В главах 4 и программировании операционных сис¬ 5 широко обсуждается параллельное програм¬ мирование. (Ge89) Gehani, N., and W. D. Roome, The Concurrent С Programming Language. Summit, NJ: Silicon Press. 1989. Первоисточник no Concurrent С льких (Ja89) задач. Jaeschke. R., Portability and the С Language, Indianapolis, IN: Ha¬ yden Books, 1989. Обсуждается написание переносимых в Джэшке принимал участие ISO С. (Ke88) С, которое обес¬ параллельной работы неско¬ надмножества печивает возможность организации работе программ комитетов по на языке ANSI С и С. по Kernighan, В. W., and D. М. Ritchie, The С Programming Langua¬ ge (Second Edition). Englewood Cliffs. NJ: Prentice Hall. 1988. Книга, ставшая в своем роде классической. С Интенсивно исполь¬ семинарах по подготовке про¬ граммистов, содержит богатый справочный материал. Ричи яв¬ ляется автором языка С и одним из создателей операционной (P192) зовалась на курсах системы UNIX. языка и на Plauger, P. J., The Standard С Library. Englewood Cliffs. NJ: Prentice.Hall. 1992. с использованием функций стандартной библиотеки С. Плогер работал начальником подкомитета при ко¬ митете, который разрабатывал стандарт ANSI С. Сейчас является главой комитета ISO, занимающегося проблемами С. Приводятся примеры (Ra90) Rabinowitz, H., and С. Schaap. Portable С, Englewood Cliffs. NJ: Prentice Hall. 1990. Книга написана в качестве носимости, читаемого тает в в учебника по курсу о проблемах пере¬ AT&T Bell Laboratories. Рабинович рабо¬ лаборатории разработки порации NYEEX, а Шаап искусственного интеллекта в кор¬ глава корпорации Delft Consulting.
52 Глава 1 (Ri78) Ritchie. D. М.; S. С. Johnson; М. E. Lesk; and В. W. Kernighan, «UNIX Time-Sharing System: The C Programming Language, The Bell System Technical Journal, Vol. 57, No. 6. Part 2. July-August 1978. pp. 1991-2019. из классических статей, описывающих язык С. Впервые опубликована в специальном выпуске Bell System Technical Jour¬ nal, посвященном операционной системе UNIX: «UNIX Ti¬ Одна me-Sharing System». (Ri84) Ritchie. D. М., «The UNIX System: The Evolution of the UNIX Ti¬ me-Sharing System,» AT&T Bell Laboratories Technical Journal, Vol. 63, No. 8, Part 2, October 1984. pp. 1577-1593. Классическая статья по системе UNIX. Впервые Bell System Technical Jour¬ операционной системе UNIX: «The операционной в специальном издании опубликована nal, полностью UNIX System.» (Ro84) посвященном Past and Fu¬ Rosier, L., «The UNIX System: The Evolution of C ture.» AT&T Bell Laboratories Technical Journal, Vol: 63, No. 8, Part 2, October 1984. pp. 1685-1699. Отлично дополняет (Ri78) и будет полезна читателям, интересу¬ историей языка С и проблемами его стандартизации. Впервые опубликована в специальном издании Bell System Tech¬ nical Journal, полностью посвященном операционной системе ющимся UNIX: (St84) «The UNIX System.» Stroustrup, B., «The UNIX System: Data Abstraction in C,» AT&T Bell Laboratories Technical Journal, Vol. 63. No. 8. Part 2, October 1984. pp. 1701-1732. статья по С++. Впервые опубликована в специаль¬ Bell System Technical Journal, полностью посвящен¬ операционной системе UNIX: «The UNIX System.» Классическая ном издании ном (St91) Stroustrup, В. The С++ Programming Language (Second Edition), Reading, MA: Addison-Wesley Series in Computer Science, 1991. Первоисточник по С++, надмножества С, включающего в себя специальные возможности для объектно-ориентированного про¬ граммирования. Страуструп разработал С++ AT&T Bell Laboratories. (To89) в лаборатории Tondo, С. L., and S. E. Gimpel, The С Answer Book, Englewood Cliffs, NJ: Prentice Hall.l989. Книга содержит ответы к упражнениям, приведенным в книге Кернигана и Ричи (Ke88). Авторы демонстрируют образцовый стиль программирования, а также обсуждают проблемы выбора схемы написания программы.
г л в а а 2 Введение в программирование на С Цели Научиться Научиться писать писать простейшие программы простые операторы ввода на С. и вывода. Познакомиться с базовыми типами данных. Понять принципы организации Научиться использованию компьютерной памяти. арифметических операций. Изучить приоритеты арифметических операций. Научиться решений. писать простейшие операторы принятия
Глава 2 54 Содержание 2.1. Введение 2.2. Простая программа 2.3. Еще одна простая программа 2.4. Общие 2.5. Арифметика 2.6. о понятия в на С: памяти печать на строки С: текста сложение чисел компьютера С Принятие решений: операции равенства Резюме двух целых и отношения Распространенные ошибки программирования Хороший стиль Советы по переносимости программ Упражнения для Ответы на упражнения для самоконтроля Упражнения программирования самоконтроля 2.1. Введение Язык С способствует структурному и дисциплинированному подходу к программ. В этой главе даны вводные замечания, писанию компьютерных на¬ ка¬ программирования на С, и представлено несколько примеров, ил¬ люстрирующих многие важные особенности языка. Каждый пример посвящен тщательному анализу какого-либо отдельного оператора. Главы 3 и 4 пред¬ ставляют собой введение в структурное программирование на С. Далее струк¬ сающиеся турный подход будет 2.2. использоваться до конца книги. Простая программа на Язык С использует нотацию, которая С: печать строки может показаться текста странной людям, никогда не программировавшим компьютеры. Мы начнем с рассмотрения до¬ печать строки текста. Про¬ вольно простой программы на С. Этот пример грамма /* и ее результат, выведенный на экран, представлены на рис. 2.1. Первая программа на С */ main() { prmtf("Welcome to C!\n"); Welcome to С! Рис. 2.1. Текст программы печати
Введение программирование на С в Несмотря 55 на простоту программы, вать некоторые важные особенности с ее помощью можно проиллюстриро¬ С. Рассмотрим подробно каждую языка строчку программы. Строка /* Первая программа начинается символами /* на */ С и заканчивается символами строка является комментарием. Программисты */, означающими, что эта вставляют в код комментарии чтобы сделать для документирования программ и для того, их более удобочитае¬ компьютера во время исполнения программы. Компилятор С просто игнорирует комментарии, не создавая для них никакого машинного объектного кода. Комментарий Пер¬ мыми. вая Комментарии программа на не оказывают никакого влияния на С просто объясняет также помогают другим назначение и людям прочитать слишком многословные комментарии могут, работу программы. понять наоборот, Комментарии вашу программу, однако затруднить ее прочтение. Распространенная ошибка программирования 2.1 Часто забывают закончить комментарий символами */ Распространенная ошибка программирования 2.2 Часто начинают комментарий символами */, а заканчивают /* символами Строка main () должна обязательно присутствовать в означают, что main каждой программе. Скобки «строительным мым функцией. Программа С может функций, однако одна из функций обязательно Хороший после main блоком» программы, называе¬ содержать одну или большее количество является должна быть main. стиль программирования 2.1 Каждая функция должна быть предварена комментарием, объясняющим ее назначе¬ ние. Левая фигурная скобка ответственно ции. правая ({) должна предварять тело каждой функции. Со¬ должна стоять в конце каждой функ¬ фигурная скобка Эта пара скобок и часть программы между ними называется блоком. Блок С. важная программная единица в Строка printf("Welcome to C!\n"); дает компьютеру команду выполнить действие, строку символов, находящуюся внутри вольной строкой, сообщением гументы внутри оператором. круглых кавычек. а именно вывести Такую строку на экран называют сим¬ или литералом. Вся строка, включая printf, ар¬ скобок и точку с запятой (;), называется Каждый оператор должен заканчиваться точкой с запятой (ино¬ оператора). Результатом выполнения опера¬ сообщения Welcome to С! на экран. Символы гда называемой символом конца тора printf обычно является вывод печатаются именно так, как они записаны внутри операторе printf. Заметьте, косая черта (\) называется что символы \n esc-символом. двойных кавычек в не появились на экране. Он указывает, что printf Обратная предстоит
56 Глава 2 выполнить нечто нестандартное. Когда встречается обратная косая черта, printf считывает следующий за ним символ и, объединяя его с обратной косой четой, создает esc-код. Esc-код \n означает новую строку, результатом являет¬ ся перевод курсора на начало следующей строки на экране. Некоторые другие esc-коды представлены на рис. 2.2. Функция printf одна из многих функ¬ в С ций, входящих (список функций стандартной стандартную библиотеку библиотеки дан в Приложении Б). Esc-код Описание VT Новая строка Перемещает курсор в начало новой строки Горизонтальная табуляция Перемещает курсор \t в следующую позицию табуляции V Возврат каретки Перемещение курсора в начало текущей строки без перемещения на следующую строку v> Звуковой системный сигнал оповещения Обратная косая черта \\ Вывод на экран символа \ при помощи оператора printf Двойные кавычки V Вывод на экран символа двойных кавычек посредством оператора printf Рис. 2.2. Некоторые распространенные esc-коды последних esc-кода на рис. 2.2. могут показаться странными. Два Дело в обратная косая черта воспринимается printf не так, как все осталь¬ ные символы; printf распознает его как ese-символ, а не как символ, который необходимо отобразить, поэтому используется двойная черта (\\), чтобы ука¬ зать на необходимость отобразить на экране одну обратную косую черту. Пе¬ чать двойных кавычек также представляет определенную проблему для printf, поскольку обычно подразумевается, что двойные кавычки обозначают том, что границы строки, и по этой причине сами по себе двойные кавычки не должны выводиться на печать. Используя esc-код \, мы сообщаем оператору printf, что необходимо вывести на печать двойную кавычку. Правая фигурная скобка (}) означает, что выполнение функции main окончено. Распространенная ошибка программирования 2.3 Вместо имени функции вывода printf в тексте программы набирают print Мы говорили, что printf вынуждает компьютер выполнить определенные действия. Выполнение любой программы представляет собой набор разнооб¬ разных действий, а также принятие решений. Конец этой главы посвящен об¬ суждению принятия решений. В третьей главе будут даны дальнейшие пояс¬ нения модели программирования действие/решение. Необходимо отметить, что стандартные библиотечные printf и scanf, пилятор не в состоянии да во время вирует работы место в функции, не являются частью языка программирования обнаружить ошибки в компилятору встречается объектной программе для такие как С. Поэтому ком¬ printf или, например, scanf. Ког¬ оператор printf, он просто резер¬ вызова библиотечной функции. Но
Введение в программирование на С компилятор знает. 57 не знает, где находится Поэтому, библиотечной библиотечная функция. Зато компоновщик когда запускается компоновщик, он находит местоположение функции и вставляет соответствующий вызов этой библиотечной функции в объектную программу. Теперь объектная программа имеет «закон¬ ченный вид» и готова к выполнению. Часто скомпонованную программу так и называют: исполняемой. Если имя функции содержит ошибку, именно компо¬ новщик ее С имени обнаружит, какой-либо Хороший стиль так как не сумеет сопоставить имя в программе на языке из существующих в библиотеке функций. программирования 2.2 какую-либо печать, последний выводимый символ дол¬ (\n). Это гарантирует, что функция оставит экран¬ Для функции, производящей жен быть символом новой строки ный курсор расположенным в начале новой строки Хороший рода облегча¬ основной задачи в Соглашения такого программного кода процессе усовершенствования программного обеспечения ют возможность повторного использования - стиль программирования 2.3 Сдвигайте тело каждой функции на один абзацный отступ (три пробела) внутрь отно¬ скобок, ограничивающих тело функции Это придает выразительность функ¬ сительно циональной структуре программ Хороший и облегчает восприятие при чтении стиль программирования 2.4 Установив соглашение на величину отступа, вы предпочтете и в дальнейшем придер¬ программ Для создания отступов можно зоваться клавишей табуляции, но позиции табуляции могут изменяться, и мендуем или использовать позиции табуляции в полсантиметра, или вводить три пробела на каждый уровень отступа живаться такого стиля написания восполь¬ мы реко¬ вручную Функция printf может напечатать Welcome to С! несколькими различны¬ способами. Например, результат выполнения программы на рис. 2.3. такой же, что и программы на рис. 2.1. Дело в том, что каждая последующая функ¬ ми ция printf возобновляет предыдущая печать с того самого места, на котором остановилась функция printf. Первая функция printf печатает Welcome и сле¬ дующий за ним пробел, вторая функция printf начинает печатать в позиции, следующей сразу за пробелом. Один оператор printf может напечатать несколько строк, если использо¬ вать символы перехода на новую строку, как показано на рис. раз, когда встречается esc-код \n (новая строка), функция 2.4. Каждый printf курсор на начало следующей строки. /* Печать в одну строку двумя вызовами printf */ main() prmtf("Welcome "); prmtf("to C!\n"); Welcome to С! Рис. 2.3. Печать в одну строку несколькими операторами printf переводит
58 Глава 2 /* Печать main нескольких строк одним вызовом printf */ () { printf("Welcome\nto\nC!\n"); } Welcome Рис. 2.4. Печать нескольких строк одним оператором printf 2.3. Еще одна простая программа двух целых чисел Следующая программа, которую мы С: сложение сейчас рассмотрим, использует стан¬ библиотечную функцию scanf, чтобы дартную на считать два целых числа, вве¬ денные пользователем с клавиатуры, вычислить сумму их значений и напеча¬ тать результат, используя функцию printf. Текст программы и образец вывода представлены на рис. 2.5. /* Программа сложения #include <stdio.h> */ main () int mtegerl, mteger2, /* объявление */ sum; /* подсказка */ &integerl); printf("Enter second integer\n"); /* прочитать целое /* подсказка */ scanf("%d", /* прочитать целое */ /* присвоить сумму */ /* напечатать first printf("Enter integer\n"); scanf("%d", &integer2); integerl + integer2; printf("Sum is %d\n", sum); = sum return Enter 0; first /* показывает успешное завершение */ сумму */ программы */ integer 45 Enter second integer 72 Sum is 117 Рис. 2.5. Программа суммирования Комментарий /* Программа мы. сложения */ объясняет назначение програм¬ Строка #include является <stdio.h> директивой для препроцессора С. Строка, начинающаяся символом #, выполняется препроцессором до того, как программа начнет компилировать¬
Введение в программирование на С 59 ся. Эта специфическая строка сообщает препроцессору, что необходимо вклю¬ чить в программу содержание стандартного заголовочного файла ввода/вывода (stdio.h). Этот заголовочный файл содержит информацию и объ¬ явления, используемые компилятором во время компиляции вызовов стандар¬ функций ввода/вывода, таких, как printf. Кроме того, заголовочный файл содержит информацию, которая помогает компилятору определить, кор¬ ректно ли написаны обращения к библиотечным функциям. Более детально со¬ держимое заголовочных файлов будет рассмотрено в главе 5. тных Хороший стиль программирования 2.5 Хотя включение в текст программы <stdio.h> не является обязательным, это необхо¬ димо делать для каждой программы С, использующей любые стандартные функции ввода/вывода При этом компилятор поможет вам обнаружить ошибки еще на ста¬ дии компиляции, а не на стадии исполнения (когда исправить ошибки значительно труднее) Как было отмечено выше, каждая программа начинает исполняться с кции main. Левая фигурная скобка ветственно конец. Строка int integer2, mtegerl, является объявлением. менных. ние, Группы Переменная то есть такие числа, как тип данных. ся sum; символов integerl, integer2 Кроме объявление имена пере¬ и sum использования переменные использованы в программе, сразу же после начинается тело фун¬ а правая соот¬ программой. Приведенное выше integerl, integer2 и sum принадлежат к в этих переменных будут храниться целые величины, 7, 11, 0, 31914 и им подобные. Прежде чем они смогут для объявление сообщает, что типу int и, следовательно, рой main, это ячейка памяти, в которую можно записывать значе¬ предназначенное быть отмечает начало тела main, int левой фигурной скобки, с кото¬ для всех переменных должны быть объявлены имя и в языке С существуют и другие типы данных. Допускает¬ нескольких переменных одного и того же типа в одном операторе. Конечно, мы могли написать три отдельных объявления для каждой перемен¬ ной, но представленный выше вариант более выразителен. Хороший стиль программирования 2.6 Вставляйте пробел после каждой запятой (,), это облегчит восприятие текста програм¬ мы Именем переменной в С может служить любой допустимый идентифика¬ это последовательность символов, включающая в себя тор. Идентификатор и символ буквы, цифры ). При этом она не должна начина¬ подчеркивания ( _ ться с цифры. На длину идентификатора не существует никаких ограничений, установленным для языка С стандартом ANSI, лишь 31 символ распознается компилятором. Язык С различает регистр, то есть буквы верхнего и нижнего регистра воспринимаются в С по-разному, поэ¬ однако согласно тому а1 и A1 требованиям, являются различными идентификаторами. Распространенная ошибка программирования 2.4 Использование заглавных букв там, где мер, вводят Main вместо main) необходимо использовать строчные (напри¬
60 Глава 2 Совет по переносимости программ 2.1 идентификаторы длиной не больше 31 символа Это га¬ рантирует переносимость и даст возможность избежать некоторых трудно обнаружимых ошибок в процессе программирования Рекомендуется использовать Хороший стиль программирования 2.7 Осмысленный рованной, Хороший то выбор есть имен переменных поможет сделать программу самодокументи- необходимых комментариев количество уменьшить стиль программирования 2.8 Первая буква идентификатора, представляющего простую переменную, должна быть будет придаваться особый смысл идентификаторам, которые начинаются с заглавной буквы, и идентификаторам, в ко¬ торых используются исключительно заглавные буквы набрана нижнем в Хороший регистре Кроме того, в книге стиль программирования 2.9 Имена переменных, состоящие из нескольких слов, помогут сделать программу легкой для восприятия Однако следует избегать слитного как например totalcommissions Лучше разделять слова черкивания total_commissions, более написания отдельных слов, при помощи символа под¬ или, если вы все же хотите писать слова слитно, на¬ чинайте каждое слово после первого с заглавной буквы, например, totalCommissH ons Объявления должны располагаться после левой фигурной скобки функ¬ ции и перед первым исполняемым оператором. Например, в программе на рис. 2.5 объявление, помещенное после первого printf, должно вызвать появ¬ ление сообщения о синтаксической ошибке. Сообщение о синтаксической ошибке появляется, когда компилятор не может распознать оператор. лятор Компи¬ сообщение, чтобы помочь программисту, найти и это нарушения неправильный оператор. Синтаксические ошибки в этом случае выдает исправить правил языка. ции или Часто ошибки синтаксические называют ошибками компиля¬ ошибками времени компиляции. Распространенная ошибка программирования 2.5 Объявление переменных между двумя Хороший образом, и исполняемые где кончаются Оператор printf("Enter first операторы одной пустой строкой, отмечая, та¬ а где начинаются исполняемые операторы объявления, integer\n"); печатает на экране символы Enter first integer следующей строки. Такое сообщение кой, операторами. стиль программирования 2.10 Разделяйте объявления ким исполняемыми и переводит курсор на начало называется приглашением или так как предлагает пользователю произвести определенные подсказ¬ действия.
Введение в программирование на С 61 Оператор scanf("%d", &mtegerl); вызывает scanf, чтобы получить от пользователя некое значение. Функция scanf считывает данные со стандартного устройства ввода, которым обычно яв¬ ляется клавиатура. В нашем случае функция scanf имеет два аргумента, % d и &integerl. Первый аргумент управляющая строка, задает формат считыва¬ ния, лю. тем самым определяется тип данных, которые предстоит ввести пользовате¬ Так, в частности, %d спецификация преобразования, вводимые данные должны быть целым числом тичных целых»). Знак % мы увидим, что это в комбинация %d (буква d в данном контексте трактуется полной мере означающая, что используется для «деся¬ scanf (в дальнейшем printf), (подобно \n). Второй относится и к как esc-код (подобно \), а аргумент scanf начи¬ которым в С задается операция взятия адреса является esc-кодом нается со знака амперсанда (&), следующей за ним переменной. Амперсанд, когда он используется совместно с именем переменной, сообщает scanf ячейку памяти, в которой хранится пере¬ менная integerl. В последующем компьютер будет хранить величину для inte¬ gerl в этой ячейке. Использование амперсанда очень часто смущает начинающих программистов, гих языках. На димо имя или людей, программировавших данный каждой переменной предварять исключения из до этого на дру¬ момент просто запомните, что в операторе этого правила знаком будут обсуждаться в главах смысл амперсанда станет ясным после того, как в главе scanf необхо¬ амперсанда. 7 6 и Некоторые 7. Настоящий мы изучим указатели. вышеприведенный оператор scanf, он ждет, переменной integerl. Пользователь после чего нажимает клавишу Return (иногда она называет¬ таким образом, число в компьютер. После этого компьютер число, или значение, переменной integerl. Любая последу¬ Когда компьютер выполняет пока пользователь не введет значение для вводит целое число, ся Enter)y посылая, присваивает данное integerl будет представлять именно это значение. printf и scanf облегчают взаимодействие между пользователем и компьютером. Ввиду того, что подобное взаимодействие походит на диалог, час¬ то такой режим работы называют диалоговым или интерактивным. ющая ссылка в программе на Функции Оператор prmtf("Enter second mteger\n"); сообщение Enter second integer, после чего переводит кур¬ следующей строки. Он также предлагает пользователю произве¬ печатает на экране сор на начало сти определенные действия. Оператор scanf("%d", получает полнения sum = &mteger2); от пользователя значение для переменной integer2. В результате вы¬ оператора присваивания mtegerl + integer2; вычисляется сумма переменных integerl и integer2, и ее значение ется переменной sum посредством операции присваивания присваива¬ Оператор читает¬ как «sum ся получает значение integerl + integer2». Большинство и вычислений выполняются при помощи операции присваивания. Операция имеет + что называются каждая операция двухместными операциями потому, по два операнда. В случае операции + двумя операндами являются integerl и двумя операндами являются sum и значение integer2. А в случае операции выражения integerl + integer2. =. = =
62 Глава 2 Хороший стиль программирования 2.11 Оставлять пробелы и с каждой стороны двухместной операции. Это выделит операции придаст программе более ясный вид. Распространенная ошибка программирования 2.6 Вычисления операторе присваивания должны находиться с правой стороны опера¬ с левой стороны операции присваивания, считаются синтаксической ошибкой = ции в Вычисления, помещенные Оператор prmtf("Sum вызывает is %d\n", sum); функцию printf, чтобы напечатать на экране текст Sum is, после ко¬ переменной sum. В данном случае printf "Sum is % d\n" и sum. Первый аргумент управляющая торого следует численное значение имеет два аргумента, строка, определяет формат вывода. Он содержит несколько символов, которые будут отображены, и спецификатор преобразования %d, определяющий, что будет напечатано целое число. Второй аргумент задает значение, которое будет напечатано. Отметим, что спецификатор преобразования для целого одинаков как для printf, так и для scanf. Это справедливо для большинства типов дан¬ ных в С. Вычисления printf. Мы могли могут выполняться и непосредственно внутри бы скомбинировать два предыдущих оператора в printf("Sum is %d\n", integerl + оператора один: integer2); Оператор return 0; операционной системы, в которой исполнялась про¬ Для операционной системы это означает, что программа завершена передает значение 0 среде грамма. успешно. Чтобы узнать, как сообщить о каких-либо сбоях в программе, изучи¬ те руководство по используемой вами операционной системе. Правая фигурная скобка означает, что функция main окончена. Распространенная ошибка программирования 2.7 Иногда забывают одну ляющую строку в или printf обе двойные кавычки, scanf в которые следует помещать управ¬ или Распространенная ошибка программирования 2.8 Забывают scanf знак % в спецификации преобразования в управляющей строке printf или Распространенная ошибка программирования 2.9 Помещают esc-код, например \n, снаружи управляющей строки printf или scanf Распространенная ошибка программирования 2.10 Задают в ражения, printf спецификации преобразования значения которых должны быть и забывают напечатаны включить в оператор вы¬
Введение в программирование на С 63 Распространенная ошибка программирования 2.11 Не включают в управляющую строку обходимо напечатать printf спецификацию преобразования, когда не¬ выражение Распространенная ошибка программирования 2.12 Помещают внутрь управляющей строки формата запятую, которая управляющую строку и выражения, которые должны быть Распространенная ошибка программирования Забывают поставить перед переменной в должна разделять напечатаны 2.13 scanf операторе знак амперсанда На многих системах эта ошибка в процессе исполнения программы назы¬ вается «ошибкой сегментации» или «нарушением доступа». Подобная ошибка происходит, когда программа пытается получить доступ к той части памяти компьютера, к которой пользовательские программы доступа не имеют. подобной ошибки будет объяснена точно причина в главе Более 7. Распространенная ошибка программирования 2.14 Перед мом В именем деле главе переменной включенной переменная 7 в ставится амперсанд, тогда как на са¬ этим знаком изучать указатели, и рассмотрим случаи, в которых предварять имя переменной знаком амперсанда, чтобы на¬ этой переменной. Однако печатать адрес printf предваряться будем мы будет необходимо ющих глав операторы 2.4. не должна printf Общие Имена переменных, на протяжении нескольких последу¬ не должны содержать знаки амперсанда. понятия о памяти такие как компьютера и sum, в действительно¬ Каждая переменная имеет integerl, integer2 сти соответствуют ячейкам в памяти компьютера. имя, тип и значение. В программе суммирования scanf("%d", на рис. 2.5, во время исполнения оператора &integerl); в ячейку памяти, которой integerl. Предположим, что пользователь вводит в качестве integerl число 45. Компьютер помещает 45 в ячейку integerl, как значение, введенное пользователем, помещается присвоено имя значения показано на рис. 2.6. Всякий раз, когда значение помещается в ячейку памяти, оно переписы¬ вает предыдущее значение, находившееся в этой ячейке. Так как предыдущая информация уничтожается, процесс ти называется разрушающим integerl информации ячейку. считывания считыванием в в ячейку 45 Рис. 2.6. Ячейка памяти, указывающая имя и значение переменной памя¬
64 Глава 2 Еще раз вернемся scanf("%d", к программе сложения, когда выполняется оператор &integer2); Предположим, пользователь вводит значение 72. Оно помещается в ячей¬ ку integer2, и как теперь выглядит память, показано на рис. 2.7. Заметим, что ячейки памяти совсем не обязательно окажутся соседними. Рис. 2.7. Ячейки integerl 45 integer2 72 памяти после ввода значений для обеих переменных После того, как программа получила значения для integerl и integer2, она складывает эти величины и помещает суммарное значение в переменную sum. Оператор, выполняющий = sum + integerl сложение integer2; считывание в ячейку. Это происходит, ког¬ да вычисленная сумма integerl и integer2 помещается в ячейку sum (стирая при этом значение, которое могло уже находиться в sum). После того как sum тоже влечет за собой разрушающее вычислена, память выглядит, как показано на рис. integerl лись. и Эти integer2 2.8. Отметим, что значения после использования в вычислении sum никак не измени¬ были уничтожены во время выпол¬ Таким образом, когда значение считывается величины использовались, но не нения вычислений компьютером. из ячейки памяти, то такой процесс называется неразрушающим считывани¬ ем из ячейки. integerl 45 integer2 72 117 sum Рис. 2.8. Ячейки памяти после вычисления 2.5. Большинство вычисления. на рис. написанных на языке в С программ С выполняют Сводная таблица арифметических операций 2.9. Отметим льзуемых в Арифметика арифметические языка С представлена использование различных специальных символов, не испо¬ алгебре. Так, звездочка (*) означает операцию взятия по модулю, означает умножение, а знак процента (%) которая будет объяснена ниже. В алгебре, мы хотим умножить а на b, мы можем просто написать переменные, обозначенные одной буквой, рядом, то есть ab. Однако если мы напишем нечто если подобное в С, то конструкция ab будет воспринята языком, как одна переменная
Введение программирование на С в 65 (или идентификатор) из двух букв. Таким образом, С (как, впрочем, и другие языки программирования) требует, чтобы умножение было явно чено с использованием знака звездочки следующим образом: а * b. многие обозна¬ ~------ Действие в С Сложение Алгебраическое выражение Выражение + f + 7 f Р-7 P - Вычитание * Умножение Деление Взятие Арифметическая операция x / х/у % по модулю b bm - или x или Хт-у Рис. 2.9. + 7 ~ * с m / у r % r mod s на С s Арифметические операции Все арифметические операции являются двухместными. Например, выра¬ жение 3 + 7 содержит двухместную операцию + и операнды 3 и 7. Результатом деления двух целых чисел также будет целое число. Например, значение выражения 7 / 4 будет 1, а 17 / 5 равно 3. В С существует операция взя¬ дает значение остатка после деления нацело. Опера¬ ция взятия по модулю может использоваться только с целыми операндами. Вы¬ ражение x % у означает остаток от деления x на у. Таким образом, результатом 7 тия по модулю, %, которая % 4 является 3, а 17 % 5 соответственно 2. В дальнейшем будет рассмотрено много интересных вариантов применения операции взятия по модулю. Распространенная ошибка программирования 2.15 Деление на ноль обычно не определено в компьютерных системах и, как правило, приводит к фатальной ошибке, то есть такой ошибке, в результате которой выполне¬ ние программы немедленно прерывается. В случае не фатальной ошибки программа выполнится до конца, однако результат выполнения программы, как правило, неве¬ рен Арифметические выражения в С должны записываться в строчку, в таком виде легче ввести программу в компьютер. Таким образом, выражения типа «а деленное наЬ» следует записывать как a/b, чтобы все операции и операнды располагались на одной строке. Алгебраическая нотация а b неприемлема, как правило, для компиляторов, хотя существуют созданные целей пакеты программного обеспечения, поддерживающие более привычную нотацию сложных математических выражений. для специальных Круглые скобки используются в выражениях на языке С точно так же, в алгебраических выражениях. Например, чтобы умножить а на b+c мы как и пишем: а * (b + с) С оценивает арифметические выражения значение) раций, которые (т.е. вычисляет их численное определяемой правилами старшинства опе¬ же, как и в алгебре: в последовательности, 3 Зак 801 в общем такие
66 Глава 2 1. Выражения в ваются выражений, находящиеся внутри скобок, оцени¬ Другими словами, могут использоваться поменять порядок вычислений на тот, что требуется или части первую скобки, чтобы очередь. программисту. Говорят, что скобки обладают «наивысшим приорите¬ В случае вложенных скобок выражение, находящееся во внут¬ том». ренней 2. паре скобок, оценивается первым. Следующими выполняются операции умножения, деления и взятия по Если в каком-нибудь выражении содержится несколько опера¬ ций умножения, деления или взятия по модулю, оценка производится слева направо. Говорят, что умножение, деление и взятие по модулю модулю. операции одинакового приоритета. 3. Последними выполняются операции сложения ражение содержит несколько производится слева операций Сложение направо. Если и вычитания. вы¬ сложения и вычитания, оценка и вычитание также операции одного приоритета. Правила, операций, определяющие приоритет щим принципом, который да оценка происходит являются тем руководя¬ С правильно оценивать выражения. Ког¬ направо, говорят об ассоциативности слева на¬ позволяет слева На право. Мы увидим, что некоторые операции ассоциативны справа налево. рис. 2.10 представлена сводная таблица старшинства операций в порядке убы¬ вания приоритета. Теперь рассмотрим несколько старшинства операций. Для равнозначная запись на Операция Действие 0 Скобки выражений Выражения + или - первую первую вычисляется значение во внутренних скобках А в случае нескольких пар есть не вложенных) скобок «одного они вычисляются направо Оцениваются в последнюю очередь Если операций несколько, то вычисления ведутся слева направо Сложение или вычитание Следующий пример в за скобками Если операций несколько, то вычисления ведутся слева направо модулю Рис. 2.10. в скобках оцениваются случае вложенных скобок в В Оцениваются вслед Умножение, деление, взятие по правил запись и очередь слева % применения алгебраическая Порядок оценки (приоритет) уровня» (то или плане С. очередь *, / в каждого примера дается Старшинство арифметических операций вычисляет среднее арифметическое для пяти элемен¬ тов: а + Ъ + с+d + e Алгебра: С: m = 5 m = (а + b + с + d + e) / 5; Скобки необходимы по той причине, что операция деления обладает более вы¬ соким приоритетом, чем сложение. Необходимо поделить сумму (а + b + с + d + e) на 5. Если скобки по ошибке пропущены, мы получим а + что приведет к вычислению совсем другого выражения b + с + d + e / 5,
Введение + а в программирование + b + с на С 67 + d 5 Еще один Алгебра: у С: у уравнение прямой линии: пример = mx + b * = m x b; + требуются. Умножение В данном случае скобки не выполняется первым, так более высокий приоритет, как операция умножения имеет чем операция сло¬ жения. Следующий пример содержит операции взятия по модулю ния, деления, сложения и вычитания: Алгебра: z С: z - pr mod q + w * = % r p / x (%), умноже¬ - у + q w / - x у; числа, расположенные под оператором, показыва¬ Обведенные кружками ют порядок, в котором С производит вычисления. Умножение, взятие по моду¬ лю и деление оцениваются в первую очередь в порядке слева направо направо), так как имеют более высокий приоритет, вычитание. Следующими выполняются операции сложения социативны слева жение и (они ас¬ чем сло¬ и вычи¬ тания, они также оцениваются слева направо. Не все выражения, женные а * в которых есть несколько пар (b + + с) * с + (d ся на «одном уровне». В Говорят, что в данном случае Чтобы лучше а * слева направо. понять правила старшинства вычисляет полином x * второй x операций, рассмотрим, как С степени. + Обведенные кружками скобки находят¬ в скобки выра¬ С вычисляет заключенные этом случае жения в первую очередь, порядок вычисления = вло¬ e) не содержит вложенных скобок. z скобок, содержат скобки. Выражение * b 4 x с ; числа, расположенные под оператором, показыва¬ ют порядок, в котором С производит оценку. В языке С нет операции возведения в степень. Поэтому мы арифметической представили x2 как x * x. Стан¬ дартная библиотека С включает в себя функцию pow возведения в степень. По причине того, что существуют некоторые тонкости применения pow, связан¬ ные с типом требуемых функцией данных, мы отложим детальное объяснение ее до главы 4. Предположим, будет вычисляться 2.6. что а = 2, b = 3, с = 7 и x = 5. Рис. 2.11 иллюстрирует, значение представленного выше полинома второй как степени. Принятие решений: операции равенства и отношения Исполняемые операторы С выполняют действия (такие, например, данных), или принимают решения (скоро мы этого). Мы можем принять решение в программе, как вычисления или ввод-вывод увидим несколько 3* примеров или
68 Глава 2 Шаг 1. у 2 = 5 * 2 * 5 + 3 5 + 7; Qo) 5 равно * * (Первая операция - самое левое умножение) r^ Шаг 2. у 10 = 10 * 5 + 3 * 5 + 7; 5 равно * ^o) (Самое левое умножение) г Шаг 3. у 50 + 3 = 3 * 5 + 7 5 равно * ^j) (Умножение перед сложением) г Шаг 4. у 50 + 15 + 7 = 50 + 15 равно^Й) (Самое левое сложение) г Шаг 5. y 65 + 7 = 65 + 7 равно ^2) (Последняя операция) Рис. 2.11. Вычисление полинома второй степени например: определить отметку некоторого человека на экзамене, окажется выше или равна шли». ры if 60, напечатать В этом разделе приведен упрощенный вариант управляющей структу¬ языка С, который позволяет программе принимать решения исходя из того, истинно или ложно некое утверждение, называемое вие выполняется (т.е. условием. Если усло¬ истинно), тело структуры if исполняется. Если же (т.е. оно ложно), то тело оператора не исполняется. оно условие не выполняется Вне и если она сообщение «Поздравляем! Вы про¬ зависимости от того, исполнилось тело оператора или нет, после заверше¬ ния выполнения оператора Условия в if выполняется следующий за ним оператор. операторе if задаются с использованием операций равенства и отношения, сводная таблица которых представлена ношения имеют одинаковый приоритет на рис. 2.12. Операции и ассоциативны слева направо. от¬ Опера¬ ции равенства имеют более низкий приоритет и также ассоциативны слева на¬ (Замечание: в С условием может какое-либо выражение, значение кото¬ либо равно нулю (false), либо нет (true). На всем протяжении книги нам право. рого будут встречаться многочисленные вие в операторе if.) примеры подобного способа Распространенная ошибка программирования Разделение символов в любой из синтаксической ошибкой. операций задавать усло¬ 2.16 ==, !=, >= или <= пробелами является
Введение в программирование Стандартная алгебраическая операция равенства или отношения на С 69 Операцияравенст- Пример условия ва или отношения в Смысл в C условия в С C Операции равенства = == X == = x равен у У i= X > > X > у x больше у < < X < у x меньше у > >= X >= у x больше или равен у < <= X <= у x меньше или равен у * x не равен у у Операции отношения Рис. 2.12. Операции равенства и отношения Распространенная ошибка программирования 2.17 Два символа в любой из операций !=, >= или <=, набранные в обратном порядке (соответственно =!, => и =<), также приведут к синтаксической ошибке. Распространенная ошибка программирования 2.18 Часто путают операцию == с операцией присваивания Чтобы избежать этой ошибки, следует как «двойное равенство», чение». Как мы увидим в читать =. знак операции а знак операции присваивания как равенства «присвоить зна¬ дальнейшем, путаница в применении этих операций распознаваемой синтаксической ошиб¬ причиной чрезвычайно трудно обнаружимых ло¬ не всегда приводит к появлению легко ки, а, напротив, может быть гических ошибок. Распространенная ошибка программирования 2.19 Иногда ставят точку с запятой непосредственно за правой скобкой, завершающей вы¬ ражение условия структуры if. Пример на рис. 2.13 использует шесть операторов if, чтобы сравнить два введенных пользователем числа. Если условие в любом из операторов if истин¬ но, то выполняется оператор printf, связанный с данным if. Программа и три варианта вывода в результате ее выполнения показаны на рисунке. Заметим, что программа на рис. 2.13 использует scanf, чтобы ввести два Каждой спецификации преобразования соответствует аргумент, в кото¬ числа. Первая спецификация %d преоб¬ в переменной numl, а вторая которое будет сохраняться в пере¬ ром будет сохраняться введенное значение. разует значение, которое должно сохраняться спецификация %d преобразует значение, менной num2. Смещая вправо тело каждого оператора if и помещая пустые строки сверху и снизу от него, мы облегчаем восприятие программы. Заметим также, что каждый оператор if на рис. 2.13. содержит в своем теле единствен-
Глава 2 70 ный оператор. В главе 3 будет показано, как записать оператор if, тело которо¬ го содержит несколько операторов. Применение операторов if, операций отношения и операций равенства #include <stdio.h> /* main () { int numl, */ num2; two pnntf("Enter I and integers, will if if if num2) (numl pnntf("%d is equal if to %d\n", (numl != num2) printf("%d is not equal (numl < целых is less (numl > num2) printf("%d is (numl to %d\n", <= than %d\n", greater than num2); numl, num2); numl, num2); num2) /* 0; num2); numl, %d\n", is less than equal or (numl >= num2) pnntf("%d is greater than return numl, показывает or to equal успешное %d\n", to numl, %d\n", завершение 3 two integers, and I will relationships they satisfy: is not equal to 7 3 is less than 7 3 is less than or Enter the equal to программы two integers, and I will relationships they satisfy: 22 is not equal to 12 the is greater than 12 22 is greater than or two integers, and I will relationships they satisfy: is 7 is equal to 7 less than or 7 is greater than equal to or equal you 7 tell you 22 12 12 to equal Enter 7 3 7 Enter 22 tell tell 7 you 7 7 to Рис. 2.13. Использование 7 операций равенства num2); numl, } the */ num2) pnntf("%d if два == pnntf("%d if you\n"); tell printf("the relationships they satisfy: "); /* прочитать scanf("%d%d", &numl, &num2); и отношения num2); */
Введение в программирование на С 71 Хороший стиль программирования 2.12 Смещать вправо операторы в теле if Хороший стиль программирования 2.13 Помещать ния Хороший В в программе пустую строку над и под каждым оператором восприятия if для облегче¬ программы стиль программирования 2.14 каждой строке программы должно быть не более одного оператора Распространенная ошибка программирования 2.20 Ставят запятые (хотя они не управляющей строке формата нужны) между спецификациями оператора scanf преобразования в считывания Комментарий к программе на рис. 2.13 разбит на две строки. В програм¬ С пробельные символы, такие как табуляции, новые строки, про¬ мах на языке белы обычно игнорируются. Поэтому разбивать операторы и коммента¬ разбивать нельзя. Таблица на рис. 2.14 показывает старшинство операций, введенных в этой главе. Операции расположены сверху вниз в порядке убывания приоритета. Заметим, что знак равенства тоже является операцией. Все эти операции за исключением операции присваивания ассоциативны слева направо. Опера¬ можно рии на несколько строк. Однако идентификаторы = ция присваивания ассоциативна справа налево. Хороший стиль программирования 2.15 Длинные операторы можно записывать в несколько строк Если оператор необходимо разбить на несколько строк, старайтесь делать это осмысленно (скажем, после запя¬ той в разделенном запятыми списке) Если оператор разбит на две или большее чис¬ ло строк, смещайте вправо все следующие за первой строки Хороший стиль программирования 2.16 При написании выражений, содержащих много операций, пользуйтесь таблицей старшинства операций. Следите за тем, чтобы операции в выражении выполнялись в правильном порядке Если вы не уверены в порядке выполнения операций в сложном выражении, используйте скобки для принудительного задания нужного порядка, так же как делаете это в алгебраических выражениях Не следует также забывать, что не¬ которые операции в С, например операция присваивания (=), ассоциативны справа налево, а не наоборот. Некоторые С в этом из слов, которые мы использовали при написании программ на параграфе в частности int, return и if являются ключевыми или зарезервированными словами языка. Полный набор ключевых слов языка С приведен на рис. 2.15. Эти слова имеют специальное значение для компилято¬ ра С, поэтому программист идентификаторы имен как чевые слова С. должен быть внимателен и не применять эти слова переменных. В книге будут рассмотрены все клю¬
72 Глава 2 Ассоциативность Операция 0 % / + V V II - ! л л и слева направо слева направо слева направо слева направо слева направо - справа налево - Рис. 2.14. Старшинство и ассоциативность рассмотренных на данный момент В данной главе программирования мы С, основываясь на пройденном турного программирования. менения отступов; с целым рядом возможностей узнаете, в и этой принятие решений. В следующей главе, главе материале, мы введем понятие струк¬ Вы подробнее познакомитесь как задавать порядок, с принципами при¬ в котором должны выпол¬ т.е. контролировать то, что называется потоком няться операторы языка включая вывод данных на экран, ввод данных пользова¬ вычислений выполнение телем, познакомились операций управле¬ ния. Ключевые слова С auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while Рис. 2.15. Зарезервированные слова Резюме Комментарии начинаются с /* и заканчиваются */. Программисты вставляют комментарии для документирования программ и их восприятия. Комментарии облегчения не оказывают никакого влияния на рабо¬ ту компьютера во время исполнения программы. Директива что препроцессора #include <stdio.h> сообщает компилятору, включить в программу стандартный заголовочный необходимо файл ввода/вывода. Этот файл содержит информацию, которая помога¬ ет компилятору определить, корректно ли написаны обращения к фун¬ кциям ввода-вывода, например, scanf и printf. из С состоит из которых обязательно должна Программа функций, одна быть main. Каждая программа начинает выполняться с функции main.
Введение в программирование на С Функция printf ной может использоваться для вывода на печать заключен¬ в кавычки строки и чения первый 73 значений выражений. При аргумент функции printf печати целого зна¬ управляющая строка, задаю¬ преобразования щая формат вывода, должна содержать спецификатор %d и, кроме того, может содержать любые другие символы, которые второй аргумент выражение, значение ко¬ торого необходимо напечатать. Если на печать будет выводиться более должны быть напечатаны; одного целого числа, управляющая строка формата должна содержать %d для каждого целого, а разделенные запятой аргументы, следующие за управляющей строкой, содержать выражения, значения которых должны быть напечатаны. Функция scanf получает значения, которые пользователь обычно вво¬ дит с клавиатуры. Ее первый аргумент управляющая строка, задаю¬ щая формат считывания, которая сообщает компьютеру, какой тип данных должен быть введен пользователем. вания % d определяет, Спецификация преобразо¬ что эти данные должны быть целыми. Каждому спецификация пре¬ образования в управляющей строке формата считывания. Имя каждой переменной обычно предваряется амперсандом (&), называемым в С операцией взятия адреса. Амперсанд при объединении с именем пере¬ менной сообщает компьютеру ячейку памяти, в которой будет хранить¬ ся значение. Затем компьютер записывает значение в эту ячейку. из оставшихся аргументов должна соответствовать в программе на С должны быть объявлены, прежде чем они будут использованы в программе. Все переменные в С может быть любой допустимый идентификатор. Идентификатор представляет собой последовательность символов, состо¬ Именем переменной ящую из букв, цифр могут начинаться с однако согласно и символа подчеркивания ( _ ). Идентификаторы не цифры. Идентификаторы могут быть любой длины; ANSI стандарту значим лишь тридцать один символ. Язык С различает регистр. Большинство вычислений выполняются при помощи операторов при¬ сваивания. Каждая переменная, хранящаяся значение и в памяти компьютера, имеет имя, тип. Всякий раз, когда новое значение помещается в ячейку памяти, оно за¬ меняет предыдущее значение, находившееся в этой ячейке. Так как эта информация уничтожается, процесс помещения информа¬ ячейку памяти назван разрушающим считыванием в ячейку. предыдущая ции в Процесс считывания считыванием из значения из памяти Чтобы облегчить введение программы выражения в С должны записываться С вычисляет вилами, называют неразрушающим ячейки. в компьютер, в строчку. арифметические выражения определяющими приоритет и арифметические в точном соответствии с пра¬ ассоциативность операций. Оператор if позволяет программисту вводить в программу возможность выбора, в зависимости от выполнения некоторого условия. Формат опе¬ ратора if следующий:
74 Глава 2 if (условие) оператор Если условие истинно, оператор в теле if выполняется. Если условие ложно, тело оператора пропускается. Условия в операторе if обычно задаются с использованием операций ра¬ венства и отношения. Результат этих операций всегда «истина» или «ложь». Заметим, что условием может порождающее нулевое (false) или быть ненулевое также (true) любое выражение, значение. Терминология С неразрушающее esc-код нуль (false) обратная косая черта \ (ese-символ) объявление ese-символ false int чтение ячейки из окончание оператора (;) main операнд stdio.h оператор true оператор присваивания амперсанд (&) аргумент операции < арифметические операции ассоциативность операций ассоциативность слева направо ассоциативность справа налево блок вложенные скобки двухместные операции <= > отношения «меньше» «меньше или равно» «больше» >= «больше или равно» операции равенства != «не равно» == «равно» операция действие операция взятия адреса деление на 0 операция взятия по деление нацело операция присваивания диалоговое вычисление операция умножения запись в строчку отступ ошибка времени компиляции ошибка при трансляции зарезервированные слова зарезервированные слова языка С звездочка процента знак равенства % (ese-символ) = (операция присваивания) значение переменной идентификатор значение имя интерактивное вычисление переменная правила старшинства различение регистра разрушающее клавиша решение enter return ключевые слова операций препроцессор С приглашение принятие решения приоритет операций пробельные символы истинность клавиша считывание новой строки (\n) символ подчеркивания ( ложность действие/решение нулю (true) не равное не фатальная ошибка в ячейку символ __ комментарий круглые скобки () литерал модель (%) (=) (*) память (*) знак модулю синтаксическая ) ошибка сообщение спецификация преобразования спецификация преобразования %d стандартная библиотека С стандартный заголовочный файл ввода/вывода
Введение в программирование на С 75 управляющая структура строка строка символов ' структурное тело тип фатальная ошибка фигурные скобки {} функция функция printf функция scanf программирование функции переменной точка с if условие запятой ; (окончание оператора) управление, поток управления управляющая строка управляющая строка целое ячейка ячейка формата памяти Распространенные ошибки программирования 2.1. 2.2. Часто забывают закончить Часто комментарий лами 2.3. начинают комментарий */. символами символами */, а заканчивают симво¬ /*. Вместо имени функции вывода printf в тексте программы набирают print. 2.4. Использование заглавных букв там, где должны быть строчные (например, вводят Main вместо main). 2.5. Объявление 2.6. Вычисления стороны 2.7. 2.8. переменных между двумя исполняемыми операторами. в =. Вычисления, помещенные присваивания, Иногда забывают одну Забывают знак % в 2.10.3адают в стороны спецификации преобразования в следует управляющей scanf. Помещают esc-код, формата printf левой обе двойные кавычки, в которые в printf или scanf. или управляющую строку или с синтаксической ошибкой. считаются помещать строке printf 2.9. операторе присваивания должны находиться с правой операции операции использованы или например \n, scanf. снаружи управляющей строки printf спецификации преобразования чить в оператор выражения, и забывают значения которых должны вклю¬ быть напе¬ чатаны. 2.11.He включают в управляющую строку зования, когда необходимо printf спецификацию преобра¬ напечатать выражение. 2.12.Помещают внутрь управляющей строки формата запятую, которая должна разделять управляющую строку и выражения, которые дол¬ жны быть напечатаны. 2.13. Забывают поставить перед переменной в операторе scanf знак ампер¬ санда. 2.14.Перед именем тогда как переменной включенной на самом деле переменная в printf ставится амперсанд, не должна предваряться этим знаком. 2.15.Деление на ноль обычно как правило, приводит к не определено в компьютерных системах и, фатальной ошибке, то есть такой ошибке, в
76 Глава 2 результате которой выполнение программы немедленно прерывает¬ фатальной ошибки программа выполнится до конца, ся. В случае не однако результат выполнения программы, как правило, неверен. 2.16. Разделение символов лами 2.17. Два любой из операций !=, ==, >= или <= любой операций !=, >= или <=, набранные (соответственно =!, => и =<), также являются символа в ном порядке в пробе¬ синтаксической ошибкой. является из в обрат¬ синтак¬ сической ошибкой. 2.18.Часто путают операцию 2.19.Иногда ставят завершающей точку с == с операцией присваивания запятой непосредственно за =. правой скобкой, выражение условия структуры if. 2.20. Ставят запятые разования scanf. Хороший стиль в (хотя они не нужны) между спецификациями преоб¬ управляющей строке формата считывания оператора программирования 2.1. Каждая функция должна быть предварена комментарием, объясня¬ ющим ее назначение. 2.2. Для функции, производящей какую-либо печать, последний выво¬ димый символ должен быть символом новой строки (\rt). Это гаран¬ что функция оставит экранный курсор расположенным в на¬ чале новой строки. Соглашения такого рода облегчают возможность основной задачи в повторного использования программного кода обеспечения. процессе усовершенствования программного тирует, 2.3. Сдвигайте тело каждой функции на один абзацный отступ (три про¬ бела) внутрь относительно скобок, ограничивающих тело функции. Это придает выразительность функциональной структуре программ и 2.4. облегчает восприятие при Установив чтении. соглашение на величину отступа, вы предпочтёте и в да¬ льнейшем придерживаться такого стиля написания программ. Для создания отступов можно воспользоваться клавишей табуляции, но табуляции могут изменяться, и мы рекомендуем или испо¬ табуляции в полсантиметра, или вручную вводить на каждый уровень отступа. пробела позиции льзовать позиции три 2.5. Хотя включение в текст программы <stdio.h> не является обязате¬ каждой программы С, использую¬ щей любую из стандартных функций ввода/вывода. При этом ком¬ вам поможет пилятор обнаружить ошибки еще на стадии льным, это необходимо делать для компиляции, а не на стадии исполнения значительно 2.7. текста после каждой запятой ( , ), это облегчит восприя¬ программы. Осмысленный выбор имен переменных поможет сделать программу самодокументированной, мых исправить ошибки труднее). 2.6. Вставляйте пробел тие (когда комментариев. то есть уменьшить количество необходи¬
Введение 2.8. в программирование на С 77 Первая буква идентификатора, представляющего простую пере¬ менную, должна быть набрана в нижнем регистре. Кроме того, в будет придаваться особый смысл идентификаторам, которые начинаются с заглавной буквы, и идентификаторам, в которых ис¬ книге пользуются исключительно заглавные буквы. 2.9. Имена переменных, состоящие из нескольких слов, помогут сделать программу более легкой для восприятия. Однако следует избегать слитного написания отдельных слов, как например totalcommissions. Лучше разделять слова при помощи символа подчеркивания to- tal_commissions, или, если вы все же хотите писать слова слитно, начинайте каждое слово после первого с заглавной буквы, напри¬ мер, totalCommissions. 2.10.Разделяйте объявления и исполняемые операторы одной пустой строкой, отмечая, таким образом, где кончаются объявления, а где начинаются исполняемые 2.11.0ставлять пробелы с операторы. каждой стороны двухместной операции. Это выделит операции и придаст программе более ясный вид. 2.12. 2.13. Смещать вправо операторы в теле if. Помещать в программе пустую строку над и под каждым оператором if для облегчения восприятия программы. 2.14.B каждой строке программы должно быть не более одного операто¬ ра. 2.15.Длинные операторы можно записывать необходимо разбить в несколько строк. Если опе¬ старайтесь делать это (скажем,после запятой в разделенном запятыми спис¬ ке). Если оператор разбит на две или большее число строк, смещайте вправо все следующие за первой строки. ратор на несколько строк, осмысленно 2.16.При написании выражений, содержащих много операций, пользуйтесь таблицей старшинства операций. Следите за тем, чтобы операции в выражении выполнялись вы не уверены в порядке выполнения нии, в правильном операций порядке. Если в сложном выраже¬ используйте скобки рядка, для принудительного задания нужного по¬ так же как делаете это в алгебраических выражениях. Не следует также забывать, что некоторые операции в С, например опе¬ рация присваивания (=), ассоциативны справа налево, а не наобо¬ рот. Советы по переносимости программ 2.1. Рекомендуется использовать возможность процессе идентификаторы с количеством симво¬ 31. Это гарантирует переносимость и даст избежать некоторых трудно обнаружимых ошибок в лов, равным или меньшим программирования.
78 Глава 2 Упражнения 2.1. для самоконтроля Заполните пустые места а) Каждая С в d) Для из следующих и заканчивается начинается с оператор заканчивается информации функция вывода лиотечная утверждений, программа начинает выполняться с функции b) Тело каждой функции c) Каждый каждом e) Esc-код \n . на экран используется стандартная биб¬ . представляет символ вод курсора в начальную позицию , следующей вызывающий пере¬ строки экрана. f) Для приема данных с клавиатуры используется стандартная биб¬ лиотечная функция . помещается в управ¬ чтобы scanf показать, что будет того, для ляющей строке формата g) Спецификация преобразования вводиться целое число, и в бы показать, h) Всякий что будет управляющей строке формата printf, что¬ выводиться целое число. раз, когда новое значение помещается в ячейку памяти, это значение переписывает предыдущее значение, находившееся в ячейке. Этот процесс известен как считывание в ячей¬ ку. i) Когда этой значение считывается из ячейки памяти и величина в ячейке сохраняется, ячейки. считыванием из то это называется используется для того, чтобы принять реше¬ j) Оператор ние. 2.2. Установите, верными; a) Когда начала являются ли следующие утверждения верными или не¬ если утверждение неверно, функция printf, вызывается объясните, почему. она всегда начинает печатать с новой строки. b) Комментарии выводить на заставляют компьютер при выполнении программы экран текст, заключенный между /* и */. c) Esc-код \n при использовании в управляющей строке формата функции printf перемещает курсор в начальную позицию следую¬ щей строки экрана. d) Все переменные должны быть объявлены, прежде чем будут испо¬ льзоваться. e) При объявлении переменной необходимо указать ее f) Язык С рассматривает переменные number и NuMbEr тип. как тожде¬ ственные. g) Объявление можно поместить в любом месте тела функции. h) Все аргументы функции printf, следующие за управляющей стро¬ кой формата, должны предваряться амперсандом (&). i) Операция взятия по модулю (%) может использоваться только с целыми операндами.
Введение в 79 программирование на С j) Арифметические /, % операции *, , +, и имеют - одинаковый прио¬ ритет. k) Верно ли следующее утверждение: следующие имена переменных идентичны всех для С, удовлетворяющих стандарту ANSI систем thisisasuperduperlongnamel2 34 5 67 thisisasuperduperlongnamel234 5 68 1) Верно необходимо использовать три опера¬ printf. тора 2.3. Как С, чтобы ли следующее утверждение: в программе на языке вывести на печать три строки, с щих одного помощью оператора С выполнить из каждое следую¬ действий: a) Объявить переменные int: с, thisVariable, q766354 типа и num¬ Закончите ваше ber. b) Предложить приглашающее вать пробел, пользователю сообщение и оставьте ввести целое (:), в позиции непосредственно за пробе¬ двоеточием курсор число. за которым должен следо¬ лом. c) Считать целое число, введенное с клавиатуры, значение в переменной а d) Если переменная number и сохранить его int. типа не равна able number is not equal to 7.». e) Напечатать сообщение «This is f) Напечатать сообщение «This is 7, сообщение «The vari¬ C program.» a а выдать С program.» в в одну две строку. строки так, чтобы первая строка заканчивалась на С. g) Напечатать сообщение «This is а С program.» так, чтобы каждое слово располагалось на отдельной строке. h) Напечатать сообщение «This is а С program.» так, чтобы каждое слово 2.4. располагалось в новой позиции табуляции. Напишите оператор (или комментарий), содержащий: a) Утверждение, целых b) Объявление, жать к будет что программа вычислять произведение трех чисел. типу что переменные x, у, z c) Приглашение пользователю ввести три d) Считать три целых числа с клавиатуры ных x, у и z. e) Вычислить result должны принадле¬ и int. и целых числа. сохранить их в перемен¬ произведение трех целых чисел, значения которых со¬ x, у, z и присвоить результирующее значе¬ держаться в переменных ние переменной result. f) Вывести на печать сообщение «The product is» значение переменной result. и следом за ним 2.5. Используя операторы, написанные в упражнении 2.4, напишите полную программу, вычисляющую произведение трех целых чисел. 2.6. Найдите и исправьте ошибки в каждом из следующих операторов:
8Q Глава 2 a) printf("The b) value (c 7) ; ("C is < less printf if (c => Ответы на is to equal or less than 7\n"); упражнения для самоконтроля а) main, b) левая фигурная скобка ({), правая фигурная скобка (}), с) точка с запятой, d) printf, e) новая строка, f) scanf, g) %d, h) раз¬ рушающее, 2.2. than 7\n"; 7) printf("C 2.1. &number); %d\n", scanf("%d%d",&numberl,number2); c) if d) is i) неразрушающее, а) Неверно. Функция printf находится курсор, а j) if. всегда начинает печать с того места, где курсор может находиться в любой позиции строки. b) Неверно. Комментарии вий тирования программ каких-либо дейст¬ Они используются для докумен¬ не вызывают выполнения во время выполнения программы. и для того, чтобы повысить их удобочитаемость. c) Верно. d) Верно. e) Верно. f) Неверно. С различает регистр, поэтому данные переменные воспримутся компилятором как две различные g) Неверно. Объявления должны размещаться после левой фигурной скобки тела функции и до любого из исполняемых операторов. h) Неверно. Аргументы функции printf обычно не предваряются ам¬ персандом. Аргументы, следующие за управляющей строкой форма¬ функции scanf, напротив, обычно предваряются амперсандом. Исключения из данного правила будут обсуждены в главах 6 и 7. та i) Верно. j) Неверно. Операции *, / и % имеют одинаковый приоритет, более низкий приоритет. рации + и k) Неверно. Некоторые длиной более 31 2.3. напечатать а) int с, оператор printf несколько строк. thisVariable, b) printf("Enter c) scanf("%d", d) if могут различать идентификаторы символа. 1) Неверно. Один жет системы а опе¬ q76354, integer: an с несколькими esc-кодами \n number; "); &a); (number != 7) printf("The variable number is e) printf("This is a C f) printf("This is a C\nprogram.\n"); not program.\n"); g) printf("This\nis\na\nC\nprogram.\n"); h) printf("This\tis\ta\tC\tprogram.\n") ; equal to 7.\n"); мо¬
Введение 2.4. в на С программирование а) /* Calculate the product of three integers */ b) int x, result; z, y, c) printf("Enter d) scanf e) result three = x * * /* Calculate "); &z); &y, z; y the integers: &x, ("%d%d%d", f) printf("The product 2.5. 81 is %d\n", of product result); three integers */ <stdio.h> #include main () { int x, z, y, printf("Enter scanf result; three result = x printf("The return integers: &x, ("%d%d%d", * * "); &z); &y, z; y is product %d\n", result); 0; } 2.6. а) Ошибка: &number. Исправление: убрать значок &. Позже будут рассмотрены исключения их этого правила. b) Ошибка: перед number2 нет амперсанда. Исправление: number2 необходимо написать &number2. Позже в в книге вместо книге будут рассмотрены исключения их этого правила. c) Ошибка: точка с запятой после правой скобки в условии операто¬ ра if. Исправление: Убрать точку с запятой после правой скобки. За¬ мечание: вие в является то, что оператор выполнен независимо от того, истинно или ложно усло¬ if. Точка с запятой правой скобки трактуется компиля¬ оператор, который ничего не делает. после тором как пустой оператор d) Ошибка: ошибки такой результатом printf будет операцию отношения => следует заменить на >=. Упражнения 2.7. Найдите и (Замечание: ошибок): a) исправьте ошибки в некоторых scanf("d", value); b) printf("The product c) firstNumber d) if (number largest e) */ в каждом из следующих операторов операторах ошибок может быть несколько + => == of %d secondNumber and = %d is %d"\n, x,y); sumOfNumbers largest) number; Program to determine the largest of three integers /* f) Scanf("%d", anInteger); g) printf("Remainder of %d divided'by %d is\n", x, y, x % y);
82 Глава 2 2.8. h) if i) print("The j) Printf("The value you enterd is: = (x у); printf(%d is equal sum Заполните пробелы a) b) Для %d\n," x каждом + из x, у); у); %d\n, &value); следующих утверждений: удобочитаемости. информации вывода c) Оператор языка это d) в %d\n", to используется для документирования программ и по¬ вышения их на экран С, предназначенный функция используется для принятия решений . Вычисления e) Функция, 2.9. is обычно выполняются при помощи операторов предназначенная для ввода данных с клавиатуры Напишите оператор С или строку, выполняющие следующие это дейст¬ вия: a) Печатает сообщение «Enter two numbers». b) Присваивает ной значение произведения переменных b и с перемен¬ а. c) Констатируйте, что программа представляет собой пример вычис¬ ления заработной платы (т.е. напишите текст, который помог бы до¬ кументировать программу). d) Введите три целых числа с клавиатуры и поместите их значения в целые переменные а, b и с. 2.10.Установите, какие из следующих утверждений верны, а какие нет. Объясните свои ответы. a) Операции в С оцениваются b) Ниже перечисленные имена слева направо. переменных являются допустимыми: _under_bar_, m928134, t5, j7, her_sales, his__account_total, z, а, b, с, z2. c) Оператор printf(<<a = 5;»); типичный пример оператора присва¬ ивания. d) Допустимое арифметическое выражение на С, не содержащее ско¬ бок, будет вычисляться слева направо. e) Все ниже перечисленные имена переменных являются недопусти¬ мыми: 3g, 87, 67h2, h22, 2h. 2.11.Впишите вместо пробелов правильный a) Какие арифметические операции умножением? ответ. имеют приоритет, одинаковый с . b) В случае вложенных скобок выражение какой пары скобок будет вычисляться в первую очередь в арифметическом выражении?
Введение в на С программирование с) Ячейка памяти компьютера, которая в процессе выполнения про¬ может граммы 83 в содержать называется разные моменты различные значения, . 2.12.Напечатается ли что-нибудь при исполнении каждого из нижеследу¬ ющих операторов С? Если ничего не напечатается, ответьте «ниче¬ го». Пусть x 2 и у 3. = = a) printf("%d", x); b) printf("%d", x x); + c) printf("x="); d) printf("x=%d", e) printf("%d f) z = + x + x g) scanf("%d%d", /* pnntf("x i) printf ("\n" ) из &x, + %d", p + i в k &b, */ С содержат считывании &c, &d, &e, в переменные, ячейку? &f); +7; c) printf("Destructive d) printf("а у); операторов разрушающем + j + x ; a) scanf("%d%d%d%d%d", b) x); + у &y); = у следующих участвующие = у, у; h) 2.13.Какие x); %d", = read-in"); 5"); = о 2.14.Дано уравнение у = b) = c) d) у у у e) у f) у = = * а * а * (а * а = а * * x) * * * x 2.15.0пределите x x* (x* + 7; + 7) ; + (x x* x* + x + a) x = b) x = c) x = x 2 (3 + 7; + 2 % * 9 будут выполняться операции в каж¬ определите, какое значение будет завершения вычислений. * 3 из 7) ; порядок, в котором после 7 какой-нибудь 7) ; дом из следующих операторов иметь ли ему С? 7; + x) x 7; соответствует операторов (x x* x) (x * * x * x (а = * x + ах ниже перечисленных a) у = * 6 / 2 2 * 2 (3 + - - (9 С, и 1; 2 / * 2; 3 / (3) ) ) ) ; 2.16. Напишите программу, предлагающую пользователю ввести два чис¬ ла, затем принимающую два числа, и выводящую на печать сумму, произведение, разность, частное и результат взятия по модулю этих чисел. 2.17. Напишите программу, выводящую на печать числа от одного до че¬ тырех в одну строку. Сделайте это следующими тремя способами:
84 Глава 2 a) Применив один оператор без спецификаций преобразова¬ printf ния. b) Применив один оператор printf спецификациями пре¬ с четырьмя образования. c) Применив четыре оператора printf. 2.18.Напишите программу, которая предлагает целых числа, большее получает эти числа и из чисел со словами жно печататься изученный в «is после пользователю ввести два этого выводит на печать larger». Если сообщение «These numbers этой главе оператора вариант же числа равны, дол¬ are equal». Используйте if. 2.19.Напишите программу на С, которая принимает три различных чис¬ ла с клавиатуры и находит их сумму, среднее, произведение, наи¬ меньшее и наибольшее. Используйте изученный в этой главе вари¬ ант оператора if. Диалог на должен экране выглядеть следующим образом: Input three different Sum is integers: 13 27 34 54 Average Product is 18 is 4914 Smallest is is Largest 13 27 считывающую радиус и выводящую на пе¬ 2.20.Напишите программу, чать диаметр окружности, ее периметр и площадь. Используйте для величину 3.14159. Выполните каждое из этих вычислений внутри printf и используйте спецификацию преобразования %f. (Замечание: в этой главе мы рассматривали только целые константы оператора и переменные. то есть В числа, 3 главе имеющие плавающей точкой, дробную десятичную часть.) мы рассмотрим числа с 2.21.Напишите программу, печатающую ромб, как показано на рисунке: 2.22. Что будет прямоугольник, овал, стрелку напечатано в результате выполнения данного и оператора? Printf ("*\n**\n***\n****\n*****\n"); 2.23. Напишите программу, которая считывает пять целых чисел, и затем определяет наибольшее и наименьшее из них. При написании поль¬ зуйтесь изучены только в теми методами программирования, которые были этой главе. 2.24. Напишите программу, считывающую целое число, а затем определя¬ ющую, четное оно или нечетное, и выводящую эту информацию на
Введение в программирование на С печать. Четное дает в (Подсказка: воспользуйтесь операцией число должно остатке 2.25.Напечатайте на ноль свои дую прописную но 85 взятия по модулю. быть кратно двум. Любое кратное двум при делении на буквами. Составьте инициалы прописными букву число два.) ей символов, из соответствующих каж¬ как показа¬ рисунке: 2.26.Напишите программу, которая считывает два целых числа, чего определяет, кратно ли первое второму, и выводит эту цию на печать. (Подсказка: воспользуйтесь операцией после информа¬ взятия по мо- дулю.) 2.27. Изобразите модель шахматной printf, а затем нарисуйте ту же доски, используя восемь операторов самую картинку, используя минима¬ льно возможное количество операторов 2.28. В чем разница между понятиями ошибка? Почему сообщение лее 2.29.B предпочтительным, этом упражнении мы printf. фатальная ошибка чем заглянем немного вперед. узнали о целых числах типа и не фатальная фатальной ошибке можно считать бо¬ сообщение о не фатальной ошибке? о int. Однако С со строчными и прописными буквами, В этой главе вы может также оперировать и значительным количеством Для внутреннего представления этих симво¬ лов С использует небольшие целые числа. Набор символов, исполь¬ специальных символов. зуемых компьютером, с соответствующим каждому символу целым числом называется печатать выполнив целое таблицей число, следующий символов компьютера. соответствующее, оператор: например, Вы можете на¬ прописной А,
Глава 2 86 printf("%d", А); Напишите программу на С, которая печатает целые числа, соответ¬ ствующие некоторым прописным и строчным буквам, цифрам и спе¬ Определите по крайней мере числа, соответст¬ * + / и символу вующие следующим символам: А В С а b с 0 1 2 $ циальным символам. пробела. 2.30. Напишите программу, которая считывает число из пяти цифр, раз¬ деляет это число на отдельные цифры и выводит эти цифры на пе¬ чать, отделяя одну от льзователь 4 2.31. 2 3 Используя ввел 3 другой тремя пробелами. Например, если 42339, программа должна напечатать. 9 только материал, изложенный используя табуляцию, выведите чений: square 0 1 4 9 16 25 36 49 64 81 100 cube 0 1 8 27 64 125 216 343 512 729 1000 данной главе, напишите кубы чисел от 0 до 10 и, следующую таблицу зна- в программу, которая вычисляет квадраты и number 0 1 2 3 4 5 6 7 8 9 10 по¬ число на печать
г л в а а з Структурная разработка программ Цели Изучить основные методики решения задач. Научиться разработке алгоритмов методом нисходящего последовательного уточнения. Научиться использованию для принятия Научиться структур выбора if и if/else решений. использованию для многократного структуры повторения while операторов програм¬ исполнения мы. Изучить методики повторения, управляемого счетчиком, и повторения, управляемого контрольным значением. Понять принципы структурного программирования. Изучить операции инкремента, декремента и присваивания.
88 Глава 3 Содержание 3.1. Введение 3.2. Алгоритмы 3.3. Псевдокод 3.4. Управляющие структуры 3.5. Структура выбора if 3.6. Структура выбора if/else 3.7. Структура 3.8. Формулирование счетчиком) 3.9. Формулирование алгоритмов на основе нисходящего пошагового уточнения: пример 2 (повторение, управляемое контрольным значением) повторения while алгоритмов: пример 1 (повторение, управляемое З.Ю.Формулирование алгоритмов на основе нисходящего пошагового уточнения: пример 3 (вложенные управляющие структуры) 3.11. Операции присваивания З.12.0перации инкремента Резюме и декремента Распространенные ошибки программирования Советы эффективности дические замечания Упражнения для самоконтроля ниям для самоконтроля Упражнения 3.1. Перед Хороший стиль Общие мето¬ программирования по повышению Ответы к упражне¬ Введение конкретной написанием программы для решения ставить себе полное представление о проблеме задачи важно со¬ и иметь тщательно спланиро¬ решению. В следующих двух главах обсуждаются методы, облегчающие разработку структурированных компьютерных программ. В раз¬ деле 4.11 мы представим краткую сводку структурного программирования, ванный подход к ее которая свяжет воедино методы, 3.2. разработанные Алгоритмы Решение любой задачи, связанной полнение ряда действий здесь и в главе 4. с вычислениями, в определенном порядке. включает в Процедура в виде 1. действий, которые надлежит выполнить, и 2. порядка, в себя вы¬ решения задачи котором эти действия должны быть выполнены,
89 Структурная разработка программ называется алгоритмом. Следующий пример показывает важность правильно¬ го определения порядка, в котором должны выполняться Рассмотрим «алгоритм активного действия. которому следует некий отправиться на работу: пробуждения», клерк для того, чтобы встать с постели и Встать с постели. Снять пижаму. Принять душ. Одеться. Позавтракать. Отправиться на работу. В результате этой последовательности действий клерк приходцт на работу хорошо подготовленным для принятия критических решений. Предположим, однако, что те же самые шаги выполняются в несколько ином порядке: Встать с постели. Снять пижаму. Одеться. Принять душ. Позавтракать. Отправиться на работу. В этом случае наш служащий появится на работе мокрым до нитки. Зада¬ ние порядка, в котором должны выполняться операторы, называется управле¬ нием (программой). В этой и следующей главах мы исследуем возможности управления программами в языке С. 3.3. Псевдокод Псевдокод это искусственный неформальный язык, который помогает разрабатывать алгоритмы. Псевдокод, который мы здесь пред¬ особенно полезен для разработки алгоритмов, которые преобразуют¬ программистам ставляем, ся затем в структурные программы на языке невный язык; он языком удобен напоминает повсед¬ хотя и не является подлинным программирования для компьютера. Программы рах. Скорее они на псевдокоде на самом деле не выполняются на компьюте¬ просто помогают программисту «продумывать» программу пе¬ попыткой написать ее С. В этой главе мы даем ред псевдокода при Псевдокод удобно С. Псевдокод и достаточно прост, на языке программирования, таком, например, как примеров эффективного использования разработке структурированных программ на языке С. несколько состоит исключительно из символов, поэтому программистам вводить программы на псевдокоде в компьютер, используя для этого редактор. Компьютер отображать требова¬ Тщательно подготовленная на псевдокоде может быть легко преобразована в соответствующую на языке С. Во многих случаях для этого достаточно простой заме¬ может на экране или печатать по нию последнюю копию программы на псевдокоде. программа программу ны операторов псевдокода их эквивалентами в Псевдокод рые будут выполняться после С. тех операторов, действия преобразования программы из псевдокода состоит только из операторов кото¬ в С и запуска. Объявления не являются исполняемыми операторами. Они пред¬ ставляют собой сообщения компилятору. Например, объявление ее
Глава 3 90 int просто i; сообщает компилятору какого-либо действия при вод или вычисление. переменной i и дает ему указание зарезер¬ переменной. Но это объявление не вызывает о типе вировать место в памяти для этой выполнении программы, как, например, ввод, вы¬ Некоторые их назначении. ным средством Повторяем, псевдокод разработки программ. 3.4. в программисты предпочитают граммы на псевдокоде перечислять все переменные с является начале про¬ кратким упоминанием об неформальным вспомогатель¬ Управляющие структуры Обычно операторы в программе выполняются один за другим в порядке их Это называется последовательным выполнением. Различные операто¬ записи. ры языка С, которые ность указать, что личаться от мы скоро следующий в очередного будем обсуждать, дают программисту возмож¬ оператор, подлежащий выполнению, может от¬ последовательности. Это называется передачей управления. В 60-е годы мых группами программного обеспечения, лежало использование передачи управления. который трудностей, испытывае¬ бесконтрольное Вина была возложена на оператор goto, стало ясно, что в основе большинства разработки позволяет программисту передавать управление в программе по одно¬ му из возможных адресов в очень широком диапазоне. мого структурного программирования goto». Исследование Бома Понятие так называе¬ стало почти синонимичным «устране¬ нию оператора и Якопини1 показало, но и при полном отсутствии операторов что программирование возмож¬ goto. Смена стиля программирования «программирование без goto» стала эпохальным девизом для программи¬ стов. Но только в 70-е годы широкие круги профессиональных программистов на Результаты оказа¬ разработки программного обеспече¬ разработки, более частой поставке сис¬ начали принимать структурное программирование всерьез. лись впечатляющими, поскольку группы ния сообщали об уменьшении времени тем в срок и завершении проектов в рамках бюджета. Ключом к успеху явля¬ ется попросту то, что программы, созданные на основе методов программирования, более понятны, их проще отлаживать и структурного модифицировать и, самое главное, более вероятно, что они написаны без ошибок. Работа Бома и Якопини показала, что все программы могут быть написа¬ ны с использованием всего трех управляющих структур, а именно последова¬ тельной структуры, структуры выбора и вательная структура, по существу, является структуры повторения. Последо¬ встроенной в язык С. Если не ука¬ зано иначе, компьютер автоматически выполняет операторы в порядке их записи. Фрагмент С один за другим блок-схемы на рис. 3.1 иллюстрирует последо¬ вательную структуру языка С. Блок-схема является графическим представлением алгоритма или части алгоритма. При рисовании блок-схем используются некоторые символы спе¬ циального назначения, такие как прямоугольники, ромбы, овалы и кружки; эти символы соединяются стрелками, называемыми линиями 1 перехода. Bohm, C., and G. Jacopini, «Flow Diagrams, Turing Machines, and Languages with Only Two Formation Rules,» Communications of theACM, Vol. 9, No. 5, May 1966, pp. 336-371.
91 Структурная разработка программ добавить grade x total total = counter total + grade = counter + 1 добавить 1 x counter Рис. 3.1. Блок-схема последовательной структуры Подобно псевдокоду, блок-схемы полезны для языка С разработки и представле¬ алгоритмов, хотя большинство программистов предпочитает псевдокод. На блок-схемах ясно видно, как действуют управляющие структуры; собст¬ ния этой книге. блок-схемы для последовательной структуры на рис. 3.1. Для указания на любой тип действия, включая вычисление или опе¬ рацию ввода/вывода, мы используем символ прямоугольника, называемый также символом действия. Линии перехода на рисунке указывают на поря¬ венно, ради этого Рассмотрим мы и используем их в часть док, в котором должны выполняться ной grade жна быть сначала значение перемен¬ действия переменной total, а затем 1 дол¬ должно быть добавлено к значению добавлена к переменной counter. Язык С позволяет нам включать в структуру произвольное число действий. Как мы скоро уви¬ может быть выполнено одно действие, можно поместить после¬ последовательную дим, везде, где действий. При рисовании блок-схемы, представляющей полный алгоритм, первым символом, используемым в блок-схеме, является символ овала, содержащий довательность из нескольких слово «Begin» («Начало»); символ овала, содержащий слово «End» («Конец») рисовании только части алгоритма, как на рис. 3.1, символы овалов опускаются и вместо них используются символы кружков, также называемые символами соединения. является последним символом. При Возможно, наиболее важным символом при составлении блок-схем явля¬ ется символ ромба, также называемый символом принятия решения, который указывает на необходимость выбора. Мы обсудим символ ромба в следующем разделе. Язык С предоставляет программисту три типа структур выбора. В струк¬ туре выбора if (раздел 3.5) некоторое действие либо выполняется (выбирает¬ ся), если условие истинно, либо пропускается, если это условие ложно. В структуре выбора if/else (раздел 3.6) некоторое действие выполняется, если условие истинно, и выполняется другое действие, если это условие ложно. В структуре выбора switch (обсуждаемой в главе 4) выполняется одно из набора различных действий в зависимости от значения некоторого выражения. Структура if называется структурой с единичным выбором, поскольку в выбирается или игнорируется одно действие. Структура if/else называет¬ структурой с двойным выбором, поскольку в ней выбор происходит между ней ся двумя альтернативными действиями. Структура switch рой со множественным выбором, льких различных действий. называется структу¬ поскольку в ней выбор происходит из неско¬
92 Глава 3 Язык С предусматривает три типа структур повторения, а именно цикл (раздел 3.7) и циклы do/while и for (оба обсуждаются в главе 4). Вот и все. В языке С имеется только семь управляющих структур: после¬ довательная, три типа выбора и три типа повторения. Любая программа на while языке С строится путем объединения такого количества управляющих струк¬ тур каждого типа, которое соответствует алгоритму, реализуемому програм¬ мой. Как и в случае последовательной структуры на рис. 3.1, мы увидим, что в каждой точке управляющей структуре входа в управляющую с управляющие структуры имеются два символа в виде кружков, один в структуру и одним входом и один в точке выхода из одним выходом делают нее. Эти написание программ довольно простым. Управляющие структуры могут присоединяться друг к другу путем соединения точки выхода одной управляющей структуры с точкой входа последующей. Это очень похоже на то, как ребенок ставит куби¬ поэтому мы будем называть это суперпозицией управляю¬ Мы щих структур. узнаем, что существует еще только один способ их соеди¬ нения метод, называемый вложением управляющих структур. Таким об¬ ки один на другой, разом, любая программа на языке писать, может щих структур, быть построена объединенных одним Структура выбора нам когда-либо потребуется на¬ из двух возможных способов. Структура выбора if 3.5. используется для направлений действий. Например, мене равен С, которую всего лишь из семи различных типов управляю¬ избрания одного из альтернативных предположим, что проходной балл на экза¬ бО. Оператор псевдокода Если оценка студента больше или равна 60 Вывести на экран «Экзамен сдан» определяет, является ли условие «оценка студента тинным или ложным. Если условие истинно, больше 60» ис¬ «Экзамен или равна на экран выводится и «выполняется» следующий по порядку оператор псевдокода (не забы¬ вайте, что псевдокод не является настоящим языком программирования). Если условие ложно, вывод на экран игнорируется и выполняется следующий по порядку оператор псевдокода. Обратите внимание, что вторая строка струк¬ туры выбора записана с отступом. Использование отступов не обязательно, но сдан» рекомендуем, поскольку это помогает акцентировать вни¬ существенных особенностях структуры программ. Мы будем уделять особое внимание применению соглашений об отступах на протяжении всей мы его настоятельно мание на книги. Компилятор белы, табуляции ния в текст языка С игнорирует пробельные символы, такие как про¬ и символы перевода строки, которые используются для введе¬ программы отступов и пустых строк. Предыдущий условный оператор псевдокода может быть написан на С как if (grade >= 60) printf("Passed\n"); Обратите внимание, что код на С близко соответствует псевдокоду. Это ляется одним из ментом свойств псевдокода, делающих разработки программ. яв¬ его таким полезным инстру¬
93 Структурная разработка программ Хороший стиль программирования 3.1 Единообразное применение разумных соглашений об отступах значительно повышает удобочитаемость программы. Мы предлагаем использовать для отступа табуляцию фиксированного размера приблизительно в 1/4 дюйма или три пробела.1 Хороший стиль программирования 3.2 Псевдокод часто используется для тирования. Затем программа Блок-схема на рис. на «продумывания» программы в процессе псевдокоде преобразуется 3.2 иллюстрирует структуру if в программу с единичным ее на проек¬ С. выбором. Эта блок-схема содержит, возможно, наиболее важный из символов, использу¬ емых при составлении блок-схем символ ромба, также называемый симво¬ лом принятия решения, который указывает на необходимость принятия реше¬ ния. Символ принятия решения содержит выражение, например, некоторое условие, которое может быть истинным или ложным. Символ принятия реше¬ ния содержит две исходящие из него линии перехода. Одна из них показывает направление, которое должно быть выбрано, если соответствующее выраже¬ ние истинно; другая показывает направление, оно ложно. В главе 2 выбираемое в том случае, если мы узнали, что решения могут приниматься на основа¬ условий, содержащих операции отношения или равенства. На самом деле, если выражение решение может быть принято на основе любого выражения нии оценивается как нулевое, то оно интерпретируется как ложное, а если оно оце¬ нивается как ненулевое, то интерпретируется как истинное. напечатать grade >=60 "Зачет" выбором Рис. 3.2. Блок-схема структуры с единичным Обратите внимание, что структура if является и одним выходом. Скоро мы узнаем, что ющих структур также содержат рехода) (кроме языка С структурой блок-схемы для с одним входом оставшихся управля¬ символов в виде кружков и только символы прямоугольников для указания на линий пе¬ действия, которые выполнить, и символы ромбов для указания на решения, которые нуж¬ но принять. Это является моделью программирования действие/решение, на нужно которой мы и акцентировали свое внимание. Можно представить себе как бы семь ящиков, в каждом из которых содер¬ жатся управляющие структуры только одного из семи типов. щие структуры пусты. В их прямоугольниках и ромбах Эти управляю¬ ничего не написано.
94 Глава 3 Задачей программиста в таком случае является сборка программы из такого количества управляющих структур каждого типа, которого требует алгоритм, объединение этих управляющих структур только одним из двух возможных способов (суперпозиция или вложение) а затем заполнение символов действий решений соответствующим алгоритму образом. Нам еще предстоит обсудить разнообразие способов написания действий и решений. и 3.6. Структура выбора if/else В структуре выбора if указанное действие да условие в истинно; противном случае выполняется только тогда, ког¬ действие пропускается. Структура дает программисту возможность указать, что в зависимости от того, является ли условие истинным или ложным, должны выполняться раз¬ личные действия. Например, оператор псевдокода выбора if/else студента больше или равна 60 Вывести на экран «Экзамен сдан» Если оценка иначе Вывести выводит на на экран экран «Экзамен «Экзамен сдан», либо выводит «Экзамен случае не сдан», сдан» не если оценка студента после вывода на экран «выполняется» псевдокода. Обратите больше или если оценка студента меньше следующий равна 60, 60. В любом по порядку оператор внимание, что тело для ветви else также записано с отсту¬ пом. Хороший стиль программирования 3.3 Делайте отступы для обеих групп операторов структуры if/else. Какое бы соглашение об отступах вы не предпочитали, оно должно после¬ Чтение программы, кото¬ об однородности интервалов, вызывает за¬ довательно применяться во всех ваших программах. рая не удовлетворяет соглашению труднения. Хороший При стиль программирования 3.4 наличии нескольких уровней отступов отступы включать одно и то же количество дополнительных Предыдущая структура псевдокода if/else для каждого уровня должны пробелов может быть написана на С как 60) (grade printf ("Passed\n"); else >= if printf("Failed\n"); Блок-схема if/else. Еще раз на рис. обратите 3.3 иллюстрирует внимание, что поток ными символами в блок-схеме являются управления в структуре стрелок) единствен¬ прямоугольники (для действий) и (кроме кружков и ромб (для решения). Мы продолжаем акцентировать внимание на этой модели вычислений действие/решение. Снова вообразите большой ящик, содержа¬ щий такое количество пустых структур с двойным выбором, какое может по-
95 Структурная разработка программ grade >=60 напечатать напечатать "Незачет" "Зачет" Рис. 3.3. Блок-схема структуры if/else с двойным выбором языка С надобиться для написания любой программы на С. Работа программиста, опять же, состоит в компоновке этих структур выбора (путем их суперпозиции и вложения) с ет алгоритм, любыми другими управляющими структурами, которых требу¬ и заполнении пустых прямоугольников и ромбов действиями и решениями, соответствующими реализуемому алгоритму. В языке С предусмотрена условная операция (?:), которая тесно связана со структурой if/else. Условная операция является единственной тернарной (трехместной) операцией языка С, поскольку для нее требуется три операнда. Операнды вместе с условной операцией образуют условное выражение. Пер¬ вый операнд является условием, второй операнд значением для всего условно¬ го выражения, если условие истинно, и третий операнд значением для всего условного выражения, если условие ложно. Например, оператор printf pnntf("%s\n", grade >= 60 ? "Passed" : "Failed"); содержит условное выражение, значением которого является рал «Passed» («Экзамен сдан»), вый литерал «Failed» управления форматом ния %s для печати printf Значениями >= строковый 60 истинно, лите¬ строко¬ grade («Экзамен не сдан»), если это условие ложно. Строка функции printf содержит спецификацию преобразова¬ если условие и символьной строки. Таким образом, предыдущий оператор выполняется, по существу, аналогично предыдущему оператору в условном if/else. выражении могут быть также действия, которые надлежит выполнить. Например, условное выражение grade >= читается так: sed\n»), 60 ? printf("Passed\n") «Если оценка больше иначе выполнить : printf("Failed\n"); или равна printf(«Failed\n»)». щей структурой if/else. Мы увидим, что в 60, Это то выполнить printf(«Pas- также сравнимо с предыду¬ некоторых операции могут быть использованы там, где операторы случаях if/else условные использовать не¬ льзя. Вложенные структуры if/else служат для проверки составных условий, if/else помещаются внутри других структур if/else. Например, следующий оператор псевдокода будет печатать А для экзаменаци¬ при этом одни структуры
96 Глава 4 онных оценок, больших или равных 90, В для экзаменационных оценок, боль¬ ших или равных 80, С для экзаменационных оценок, больших или равных 70, D для экзаменационных оценок, больших или равных 60, и F для всех других оценок. Если оценка больше студента или равна 90 Вывести «А» иначе Если оценка студента больше Вывести «В» или равна 80 иначе Если оценка студента больше Вывести «С» или равна 70 иначе Если оценка студента больше Вывести «D» или равна 60 иначе Вывести «F» Этот псевдокод if (grade >= может быть написан на С в виде 90) printf("A\n"); else if (grade >= 80) printf("B\n"); else if (grade >= 70) printf("C\n"); else if (grade >= 60) printf("D\n"); else printf("F\n"); Если переменная grade больше или равна дут истинными, однако выполняться будет 90, первые четыре условия бу¬ только оператор printf после пер¬ вой проверки. После выполнения этого оператора printf ветвь else «внешнего» оператора if/etee игнорируется. Многие программисты на С предпочитают за¬ писывать предыдущую структуру if (grade >= if в виде 90) printf("A\n"); else if (grade >= 80) printf("B\n"); else if (grade >= 70) printf("C\n"); else if (grade >= 60) printf("D\n"); else printf("F\n"); С точки зрения компилятора няя С обе формы являются эквивалентными. форма широко распространена, поскольку льных отступов вправо при написании кода. она позволяет Послед¬ значите¬ При наличии таких отступов в разбиение строк и ухудшает строке часто остается мало места, что вызывает читаемость программы. избегать
97 Структурная разработка программ В теле структуры выбора if предполагается наличие только одного операто¬ Чтобы включить в тело структуры if несколько операторов, заключите этот ра. операторов в фигурные скобки ({ и }). Совокупность операторов, содержа¬ щихся внутри пары фигурных скобок, называется составным оператором. набор Общее методическое замечание 3.1 Составной оператор может быть помещен в любое место программы, где может сто¬ ять простой оператор. Следующий if/else. if пример содержит составной оператор в ветви else структуры >= (grade 60) printf("Passed.\n"); else { printf("Failed.\n"); pnntf("You must take } В этом случае, again.\n"); course если переменная printf оба оператора this grade меньше 60, программа выполняет внутри ветви else и выводит Failed. You must take в again. course в которые заключены оба опера¬ предложении else. Эти фигурные скобки важны. Без фигурных скобок Обратите тора this внимание на фигурные скобки, оператор printf("You must оказался бы вне тела от того, меньше 60 take this course again.\n"); else-ветви структуры if значение grade и выполнялся Распространенная ошибка программирования Пропуск одной или бы вне зависимости или нет. 3.1 обеих фигурных скобок, ограничивающих составной оператор Синтаксические ошибки проявляются ошибки во время Логические компилятором. Фатальные логические ошибки вылавливаются выполнения. причиной отказа в работе программы и ее преждевременного завер¬ шения. Логическая ошибка, не являющаяся фатальной, допускает дальней¬ шее выполнение программы, но при этом программа дает неправильные резу¬ льтаты. являются Распространенная ошибка программирования 3.2 Помещение точки с запятой после условия в условной структуре приводит ошибке в структурах if с одним выбором и к синтаксической ошибке ской рах if с стиль программирования 3.5 Некоторые программисты предпочитают скобки скобок 4 Зак 801 структу¬ двойным выбором Хороший ные к логиче¬ в вводить начальную и завершающую фигур¬ операторах до начала ввода операторов внутри фигурных Это помогает избежать пропусков одной или обеих фигурных скобок. в составных
98 Глава 3 Общее методическое замечание 3.2 Подобно тому, как составной оператор можно помещать всюду, где может быть по¬ мещен обще, с простой оператор, так же возможно не помещать там никакого то есть использовать запятой (;) на месте, оператора во¬ пустой оператор Пустой оператор представляется точкой обычно занимаемом некоторым операюром В этом разделе мы ввели понятие составного оператора. Составной опера¬ тор может содержать объявления (как это происходит, например, в теле функ¬ ции main). В этом случае составной оператор называется блоком. Объявления в блоке должны помещаться в начало блока перед любыми операторами дейст¬ Мы обсудим использование блоков в главе 5. До этого времени читателю вия. следует кции избегать использования блоков (конечно, это не относится к телу фун¬ main). 3.7. Структура Структура повторения while повторения гократное выполнение истинным. Оператор позволяет действия программисту специфицировать мно¬ до тех пор, пока некоторое условие остается псевдокода Пока в моем списке покупок еще остаются пункты Сделать следующую покупку и вычеркнуть соответствующий пункт описывает повторяющиеся купками. Условие дейстзия, происходящие во время поездки за по¬ «в моем списке покупок еще остаются пункты» может истинным или ложным. Если оно истинно, то выполняется быть действие «Сделать следующую покупку и вычеркнуть соответствующий пункт из списка». Это действие будет многократно выполняться до тех пор, пока условие будет оста¬ ваться истинным. Оператор(ы), содержащийся в структуре повторения while, составляет тело этой структуры. Тело структуры while может быть простым или составным оператором. В конце концов условие становится ложным (когда последний предмет списке покупок завершается и приобретен и вычеркнут из выполняется первый списка). В оператор в этот момент повторение псевдокода, следующий за структурой повторения. Распространенная ошибка программирования 3.3 Отсутствие в теле структуры while действия, которое в конечном итоге приводит к тому, что условие в структуре while становится ложным Обычно такая структура по¬ возникает ошибка, называемая «бесконеч¬ вторения никогда не будет завершена - ным циклом» Распространенная ошибка программирования 3.4 Написание ключевого слова while с W в верхнем регистре в виде While (не забывай¬ регистр) Все зарезерви¬ рованные ключевые слова языка С, такие как while, if и else, содержаттолько симво¬ лы нижнего регистра те, что С является языком программирования, различающим
99 Структурная разработка программ В качестве примера реальной структуры while рассмотрим фрагмент про¬ граммы, разработанной для нахождения первой степени 2, превосходящей 1000. Предположим, что целочисленная переменная product была инициали¬ зирована значением 2. После завершения повторения while = product выполнения следующей структуры переменной product будет содержаться желаемый ответ: в 2; while <= (product 2 product * = 1000) product; Блок-схема на рис. 3.4 иллюстрирует поток управления в структуре повто¬ while. Еще раз обратите внимание, что (кроме кружков и стрелок) рения блок-схема содержит только символ прямоугольника и символ ромба. Снова во¬ образите себе большой ящик пустых структур while, которые путем суперпози¬ ции или вложения в другие управляющие структуры могут образовывать структурную реализацию потока управления алгоритма. Пустые прямоуголь¬ ники и ромбы Блок-схема затем заполняются соответствующими отчетливо демонстрирует повторение. действиями и решениями. Линия перехода, выходящая снова возвращается к блоку принятия решения, условие которого проверяется на каждом проходе цикла до тех пор, пока оно в конечном итоге не становится ложным. В этот момент происходит выход из структуры из прямоугольника, while, и При управление переходит к следующему входе в структуру while значение оператору программы. переменной product равно 2. Пере¬ product многократно умножается на 2, последовательно принимая значения 4, 8, 16, 32, 64, 128, 256, 512 и 1024. Когда значение переменной product становится равным 1024, условие в структуре while, product <= 1000, менная становится ложным. ной Это завершает повторение, и конечное значение перемен¬ равно 1024. Выполнение программы продолжается с оператора, следующего за структурой while. product Рис. 3.4 Блок-схема структура повторения while 3.8. Формулирование алгоритмов: пример (повторение, управляемое счетчиком) Чтобы проиллюстрировать, как разрабатываются алгоритмы, мы решим в средней оценки в группе. Рассмотрим нескольких вариантах задачу о подсчете задачу в следующей постановке: 4* 1
100 Глава 3 Группа из десяти студентов сдала экзамен. В вашем распоряжении име¬ (целые числа в диапазоне от 0 до 100), полученные студен¬ этом экзамене. Определите среднюю оценку за экзамен для ются оценки тами на группы. Средняя оценка равна сумме оценок, деленной на число студентов. Алгоритм для решения этой задачи на компьютере должен предусматривать ввод каждой оценки, расчет среднего и вывод результата. Давайте воспользуемся псевдокодом и составим перечень действий, подле¬ жащих выполнению, и определим порядок, в котором эти действия должны быть выполнены. Для ввода оценок по одной за раз мы используем повторение, управляемое счетчиком. Этот метод использует переменную, называемую счет¬ чиком, определяющую, сколько раз должна выполняться последовательность операторов. В этом примере повторение завершается, когда значение счетчика становится больше 10. В этом разделе мы просто представим алгоритм на псев¬ (рис. 3.5) и соответствующую программу на С (рис. 3.6). В следующем разделе мы покажем, как разрабатываются алгоритмы на псевдокоде. Повторе¬ докоде ние, управляемое счетчиком, часто называют скольку число Обратите повторений определенным повторением, по¬ известно до начала выполнения цикла. внимание на ссылки в алгоритме на итоговую сумму и счетчик. Итоговая сумма представляет собой переменную, которая используется для накопления суммы ряда значений. Счетчик это переменная, которая испо¬ льзуется для подсчета, в данном случае для подсчета числа введенных оценок. используемые для хранения итоговых сумм, обычно должны инициализироваться нулем перед суммированием; иначе сумма включала бы предыдущее значение, которое хранилось в ячейке памяти итоговой суммы. Переменные, Переменные-счетчики обычно инициализируются нулем или единицей в зави¬ конкретного алгоритма (мы представим примеры, иллюстрирую¬ щие каждый из вариантов). Неинициализированная переменная содержит в качестве значения «мусор» значение, которое последним хранилось в заре¬ симости от зервированной для нее ячейке памяти. Распространенная ошибка программирования 3.5 Если счетчик или сумма не инициализированы, результаты работы вашей вероятности, некорректными Это является примером логи¬ итоговая программы будут, ческой ошибки по всей Установить итоговую сумму Установить счетчик оценок Пока счетчик оценок меньше в в ноль. единицу. или равен десяти следующую оценку Прибавить эту оценку к итоговой Ввести Прибавить единицу к сумме счетчику оценок оценке в группе значение на деленной десять суммыу Вывести среднюю оценку в группе Присвоить средней Рис. 3.5. Алгоритм на псевдокоде, использующий повторение, управляемое средней оценки в группе для решения задачи о подсчете счетчиком,
101 Структурная разработка программ /* подсчета Программа с повторением, средней оценки управляемым в счетчиком группе */ #include <stdio.h> main () { int counter, total, grade, average; /* этап инициализации */ total 0; counter 1; = = /* этап while ( обработки */ counter <= 10 ) { printf("Enter grade: "); scanf("%d", &grade); total total + grade; = counter - + counter 1; } /* этап average завершения = total printf("Class return 0; /* */ / 10; average is показывает, %d\n", что average); программа успешно завершена */ } Enter grade: 98 Enter grade: 76 Enter grade: 71 Enter grade: 87 Enter grade: 83 Enter grade: 90 Enter grade: 57 Enter grade: 79 Enter grade: 82 Enter grade: 94 Class average is Рис. 3.6. Написанная на С программа подсчета средней оценки в группе с повторением, управляемым счетчиком, и пример ее выполнения Хороший стиль программирования 3.6 Инициализируйте счетчики и итоговые суммы Обратите внимание, что расчет среднего в программе дал целочисленный результат. На самом деле сумма оценок в этом примере равна 817, которая, бу¬ дучи поделенной на 10, должна дать 81.7, т.е. число с десятичной дробью. В следующем разделе мы увидим, как обращаться с такими числами (называе¬ мыми числами с плавающей точкой).
102 Глава 3 3.9. Формулирование алгоритмов на основе уточнения: пример 2 нисходящего пошагового (повторение, управляемое контрольным значением) Давайте обобщим задачу о подсчете средней оценки в группе. Рассмотрим следующую задачу: Разработать программу для подсчета средней оценки в группе, которая при каждом своем запуске будет обрабатывать произвольное число оце¬ нок. В первом примере для подсчета средней оценки в группе число оценок известно заранее. В настоящем примере не дается никаких указаний (10) было относите¬ быть введено оценок. Программа должна обрабаты¬ число. Каким образом программа сможет определить, льно того, сколько должно вать произвольное их когда ввод оценок должен быть должна быть вычислена Один из прекращен? Каким образом она узнает, и выведена на экран средняя оценка в способов решения этой проблемы состоит в том, когда группе? чтобы использо¬ вать для указания на «конец ввода данных» специальное значение, называе¬ (другие названия: сигнальное флаговое значение). Пользователь вводит мое контрольным значением значение, фиктив¬ оценки до тех пор, пока не будут введены все «правильные» оценки. После этого пользователь вводит контрольное значение, чтобы показать, что была введена последняя ное значение или Повторение, управляемое контрольным значением, часто называют неопределенным повторением, поскольку число повторений неизвестно до на¬ оценка. чала выполнения цикла. Очевидно, что контрольное значение должно выбираться таким образом, чтобы его нельзя было спутать с допустимым входным значением. Поскольку экзаменационные оценки обычно являются неотрицательными целыми числа¬ ми, приемлемым контрольным значением для этой задачи будет -1. Таким об¬ разом, при запуске программы подсчета средней оценки в группе может быть обработан поток входных значений, например, 95, 96, 75, 74, 89 и -1. После этого программа вычислит и выведет на экран среднее значение по группе для оценок 95, 96, 75, 74 и 89 (-1 является контрольным значением, поэтому оно не должно учитываться при расчете среднего). Распространенная ошибка программирования Выбор нием в качестве 3.6 контрольного такого значения, которое является допустимым значе¬ данных Наш подход к написанию программы для подсчета средней оценки в груп¬ будет опираться на метод, называемый нисходящим пошаговым уточнени¬ ем, и который является неотъемлемой частью разработки хорошо структури¬ пе рованных программ. Мы начнем с представления псевдокода для верхнего уровня нисходящего процесса: Определить среднюю оценку в группе за экзамен уровень представляет собой одно предложение, выражающее общее назначение программы. Как таковой, верхний уровень фактически полностью представляет программу. К сожалению, верхний уровень (как в этом случае) Верхний
103 Структурная разработка программ редко несет в себе достаточное количество подробностей, необходимых для на¬ писания программы на С. Итак, теперь уровень на ряд мы начинаем процесс уточнения. более Мы подразделяем верхний мелких задач и перечисляем их в том порядке, в котором они должны выполняться. Это приводит к следующему первому уточнению. Инициализировать переменные Ввести, просуммировать и подсчитать Вычислить и вывести на экран количество оценок оценку для группы среднюю Здесь имеет место последовательная структура должны выполняться по порядку, один за другим. перечисленные шаги Общее методическое замечание 3.3 Каждое уточнение, так же как и сам верхний уровень, представляет собой полную спецификацию алгоритма, меняется только уровень детализации Для перехода к следующему уровню детализации, то есть ко второму уточ¬ нению, мы вводим конкретные переменные. Нам нужна текущая сумма чисел, счетчик количества обработанных чисел, переменная для приема значения очередной вводимой оценки и переменная, в ное среднее значение. Оператор псевдокода Инициализировать которой содержится рассчитан¬ переменные может быть уточнен следующим образом: Инициализировать итоговую сумму Инициализировать счетчик нулем нулем внимание, что нужно инициализировать только итоговую сумму и счетчик; переменные average и grade (соответственно для рассчитанного сред¬ него значения и ввода пользователя) инициализировать не обязательно, по¬ Обратите скольку их значения перезаписываются разрушением информации, обсуждаемого в результате в главе процесса ввода с 2. Для оператора псевдоко¬ да Ввести, просуммировать потребуется подсчитать структура повторения дится каждая оценка. торое и должно быть Поскольку (т.е. цикл), мы значением. допустимые оценки. После ввода в которой оценок последовательно вво¬ мы не знаем заранее количества оценок, ко¬ обработано, управляемое контрольным количество будем использовать Пользователь по последней допустимой повторение, одной будет вводить оценки пользователь Программа будет осуществлять проверку после будет введено контрольное значе¬ предыдущего оператора псевдокода тогда будет введет контрольное значение. ввода каждой оценки и завершит цикл, когда ние. Уточнением Ввести первую оценку Пока пользователь не ввел контрольного значения Прибавить эту оценку к текущему итогу Прибавить единицу к счетчику оценок Ввести следующую оценку (возможно, контрольное значение)
104 Глава 3 Обратите с тело операторов, образующей все эти операторы под просто выравниваем принадлежат этой структуре while. формальное вспомогательное Оператор псевдокода while, чтобы Повторим, что разработки средство Вычислить и вывести на экран может фигурные скобки внимание, что в псевдокоде мы не используем последовательностью среднюю структуры while. Мы показать, что все они это только не¬ псевдокод программ. оценку для группы быть уточнен следующим образом: Если счетчик не равен нулю Присвоить переменной Вывести average сумму, деленную на экран average на экран «Не было ввода оценок» на счетчик иначе Вывести Обратите внимание, что мы действуем здесь достаточно аккуратно и проверяем возможность деления на ноль фатальной оставшись невыявленной, способна привести к отказу в (часто называемому рис. 3.7. «крахом»). Полностью ошибки, которая, работе программы второе уточнение показано на Распространенная ошибка программирования 3.7 Попытка деления на ноль, являющаяся причиной фатальной ошибки Инициализировать Инициализировать итоговую сумму нулем счетчик Ввести первую оценку Пока пользователь Прибавить не нулем ввел эту оценку контрольного значения к текущему итогу счетчику оценок Прибавить единицу к Ввести следующую оценку (возможно, контрольное значение) Если счетчик не равен нулю Присвоить переменной Вывести average сумму, деленную на экран average на экран «Не было ввода оценок» на счетчик иначе Вывести Рис. 3.7. Алгоритм на псевдокоде, использующий повторение, управляемое контрольным средней оценки в группе значением, для решения задачи о подсчете Хороший стиль программирования 3.7 При выполнении деления на выражение, значение которого может быть нулем, явно осуществляйте проверку этого случая и обрабатывайте его соответствующим образом (например, выводите сообщение об ошибке), и не допускайте возникновения фата¬ льной ошибки
105 Структурная разработка программ На рис. 3.5 и 3.7 мы включаем в псевдокод несколько пустых строк для удобочитаемости. Фактически пустые строки разделяют эти программы на различные этапы их выполнения. повышения его Общее методическое замечание 3.4 Многие программы логически могут быть разделены на три этапа' этап инициализа¬ ции, на котором происходит инициализация переменных программы; этап обработ¬ ки, на котором происходит ввод данных и соответствующая программы, и ние окончательных вывод Алгоритм средней счете разработан на псевдокоде, настройка переменных программы, на котором происходит вычисле¬ работы и этап завершения результатов приведенный на рис. 3.7, решает задачу о под¬ общей постановке. Этот алгоритм был двух этапов уточнения. Иногда требуется боль¬ оценки в группе в более после всего лишь шее количество этапов. Общее методическое замечание 3.5 Программист завершает процесс нисходящего пошагового уточнения в тот момент, когда алгоритм на псевдокоде специфицирован достаточно подробно для того, чтобы программист мог преобразовать псевдокод в программу на С на С в этом случае обычно не вызывает затруднений Реализация программы На рис. 3.8 показана программа на С и пример ее выполнения. Хотя вво¬ дятся только целочисленные оценки, при расчете среднего может получиться дробное число. Типом int такое число представлено ме применяется тип данных float для обработки быть не может. чисел с В програм¬ десятичной дробью (называемых числами с плавающей точкой) и специальная операция, называ¬ емая операцией приведения типа, для управления расчетом среднего значе¬ ния. Более подробно эти понятия объясняются после представления програм¬ мы. Обратите внимание на составной оператор в цикле while на рис. 3.8. По¬ вторим, что фигурные скобки необходимы для того, чтобы выполнялись все четыре оператора внутри цикла. Без фигурных скобок последние три операто¬ ра в теле цикла вышли бы за его пределы, приводя к неправильной интерпре¬ тации этого кода: while (grade != -1) total total + grade; = counter = counter +1; -1 printf("Enter grade, scanf("%d", &grade); to end: Получается бесконечный цикл, первой "); если пользователь не вводит -1 в качестве оценки. Хороший стиль программирования 3.8 В цикле, управляемом контрольным значением, подсказки для запроса ввода данных должны явно Средние напоминать пользователю, равно это чему значение величины не всегда выражаются целочисленными значениями. Часто среднее является значением типа 7.2 или -93.5, которое содержит
106 Глава 3 дробную часть. Эти значения называются представляются типом данных at, чтобы ^н дробную не потерять /* Программа с #include mam с плавающей точкой как оценки управляемым в группе контрольным значением */ <stdio.h> () { float int /* этап total /* average; = counter новый тип данных */ total; grade, counter, */ инициализации 0; 0; = /* этап обработки */ printf("Enter grade, scanf("%d", &grade); -1 to end: "); while ( grade != -1) { total total + grade; = counter counter = + pnntf("Enter grade, scanf("%d", &grade); 1; -1 to end: "); } /* if этап завершения != (counter 0 */ { (float) total / counter; average pnntf ("Class average is %.2f", average) ) = ; } else printf("No grades return 0; /* were показывает, entered\n"); что программа успешно завершена */ } Enter Enter grade, grade, grade, grade, grade, grade, grade, grade, grade, Class average Enter Enter Enter Enter Enter Enter Enter -1 -1 -1 -1 -1 -1 -1 -1 -1 is to end: 75 to end: 94 to end: 97 to end: 88 to end: 70 to end: 64 to end: 83 to end: 89 to end: -1 82.50 средней оценки в группе с повторением, управляемым контрольным значением, и пример ее выполнения Рис. 3.8. Написанная на С программа подсчета и flo¬ часть результата нашего вычисления. средней подсчета повторением, числами float. Переменная average объявлена
107 Структурная разработка программ Однако результат вычисления total скольку обе переменных total и / counter counter является целым числом, по¬ являются целочисленными. двух целых чисел приводит к целочисленному делению, при котором часть результата теряется (т.е. происходит его усечение). Поскольку Деление дробная сначала дробная часть теряется до присваивания результата пе¬ ременной average. Для деления с плавающей точкой целочисленных значений выполняется деление, мы должны создать для них временные значения, которые являются числами с плавающей точкой. В С Для решения этой задачи предусмотрена одноместная операция языке типа. приведения включает Оператор (float) = average total / counter; себя операцию приведения типа (float), которая создает для своего total копию с плавающей точкой. Использование опера¬ операнда временную в ции приведения типа подобным образом называется явным преобразованием. Значение, хранимое в переменной total, по-прежнему является целым числом. Вычисление теперь состоит в делении значения с плавающей точкой ная «копия» ной counter. (времен¬ float) на целочисленное значение, хранимое в перемен¬ Компилятор С умеет оценивать лишь выражения с идентичными total типа типами данных операндов. Чтобы гарантировать совпадение типа операндов, компилятор выполняет над цию возведения типа (или выбранными операндами так называемую опера¬ преобразование). Например, в выражении, неявное содержащем типы данных int и float, согласно стандарту ANSI, для операндов типа int делаются копии, которые возводятся до типа float. В нашем примере после создания копии переменной counter и ее возведения до типа float произ¬ плавающей точкой присваивается предусматривается набор правил возве¬ дения для операндов различных типов. В главе 5 представлено обсуждение водится вычисление, переменной average. и результат деления с Стандартом ANSI всех стандартных типов данных и порядка их возведения. Операции приведения типа существуют для любого типа данных. Опера¬ образуется путем помещения имени типа данных в круг¬ лые скобки. Операция приведения типа является одноместной операцией, т.е. операцией, принимающей только один операнд. В главе 2 мы изучали двухме¬ ция приведения типа стные арифметические операции. В С также поддерживаются одноместные варианты операций плюс (+) и минус (-), поэтому программист может напи¬ сать выражения вроде -7 или +5. Операции приведения типа ассоциируются справа налево и имеют тот же приоритет, что и другие одноместные операции, как, например, уровень выше приоритета (унарные) Этот приоритет на один унарный мультипликативных операций *, / и % и на один унарный 4- и -. круглых скобок. В программе на рис. 3.8 для вывода значения переменной average исполь¬ порядок ниже приоритета зуется спецификатор преобразования % .2f функции printf. Символ f указыва¬ ет, что будет выведено значение с плавающей точкой. Символ .2 представляет собой что чной ния которой будет отображено это значение. Она устанавливает, будет отображено с 2 десятичными знаками справа от десяти¬ точность, с значение точки. Если используется спецификатор преобразования %f (без указа¬ то по умолчанию используется точность 6 как если Ьы был точности), указан спецификатор преобразования % .6f. Когда значения с кой выводятся с указанием точности, выводимое значение плавающей точ¬ округляется до
108 Глава 3 заданного числа десятичных знаков. Значение в памяти остается неизменным. При выполнении следующих операторов выводятся значения " % 2f\n" , 3.446); /* выводит 3.45 */ pnntf("%.lf\n", 3.446); /* выводит 3.4 */ pnntf ( . 3.45 и 3.4. Распространенная ошибка программирования 3.8 Указание точности для спецификации преобразования в строке управления форма¬ функции scanf является ошибочным Точность используется только в специфика¬ том циях преобразования функции printf. Распространенная ошибка программирования 3.9 Предположение, что числа с плавающей точкой ютера с абсолютной точностью, может привести льшинстве компьютеров числа с будут представлены в памяти компь¬ к неправильным результатам плавающей точкой представляются только На бо¬ приблизи¬ тельно Хороший Не стиль программирования 3.9 проверяйте Несмотря на равенство значения на то, что числа с плавающей точкой с плавающей точкой не всегда «на 100% точ¬ Например, когда мы говорим о ны», они имеют многочисленные приложения. «нормальной» температуре тела в на точность, представляемая зывает нам температуру в 98.6 градусов (по Фаренгейту), большим числом знаков. 98.6 градусов, нам не нуж¬ Когда термометр на самом деле она может пока¬ быть равна просто 98.6 98.5999473210643. Суть в том, что принятие этого числа равным прекрасно подходит для большинства приложений. Позже мы обсудим вопрос более подробно. Другой причиной, по ляется операция деления. ло 3.3333333... ютер выделяет с которой возникают Когда мы делим 10 этот плавающей точкой, яв¬ 3, результатом является чис¬ числа с на бесконечной повторяющейся последовательностью 3. Компь¬ область памяти для его хранения, поэтому лишь ограниченную очевидно, что значение с плавающей точкой может быть только приближен¬ ным. 3.10. Формулирование алгоритмов на основе уточнения: пример 3 управляющие структуры) нисходящего пошагового (вложенные Давайте найдем полное решение еще одной задачи. Мы еще раз сформули¬ для этого псевдокод и процесс нисходящего пошаго¬ вого уточнения, и напишем соответствующую программу на С. Мы уже виде¬ руем алгоритм, используя ли, что управляющие структуры могут ДРУГУЮ, подобно тому, как увидим ребенок единственный отличный от предыдущего соединяться управляющие структуры, щей структуры в другую. (последовательно) помещаться одна на кубики. В этом примере мы укладывает а именно способ, которым в С могут одной управляю¬ вложение
109 Структурная разработка программ Рассмотрим следующую постановку задачи: предлагает студентов к сдаче на диплома экзамена государственного получение брокера по недвижимо¬ сти. В прошлом году несколько студентов из прослушавших этот курс платный курс для подготовки Колледж экзамен на получение диплома. Естественно, что колледж хо¬ бы знать, насколько успешно его студенты сдали этот экзамен. Вас попросили написать программу для обобщения результатов экзаме¬ сдавали тел на. Вам дали студента мен список 10 этих проставлена i, если каждого студентов. Напротив фамилии студент сдал экзамен, и 2, если он экза¬ сдал. не Ваша программа должна анализировать результаты экзамена следующим об¬ разом: 1. Ввести результат каждого ном запросе «Введите экзамена экзамена результата (т.е. 1 или 2). При каждом очеред¬ отображать на экране сообщение результат». 2. Подсчитать количество результатов каждого типа. 3. краткую сводку результатов экзамена, указав количество студентов, сдавших экзамен, и количество студентов, его не сдавших. Отобразить 4. Если экзамен сдали более 8 студентов, вывести сообщение «Повысить плату за курс». После тщательного изучения постановки задачи мы можем сделать следу¬ ющие наблюдения: 1. Программа зован обработать 10 результатов управляемый счетчиком. должна цикл, экзамена. Будет исполь¬ 2. Каждый результат экзамена представляет собой число, 1 или 2. Всякий раз, когда программа считывает результат экзамена, она должна опре¬ делять, является ли это число 1 или 2. В нашем алгоритме мы проверя¬ ем на нении 1. Если в число не равно этой конце 1, главы мы предполагаем, что это рассматриваются 2. (В упраж¬ последствия такого предположения.) 3. Используются два счетчика сдавших экзамен, и другой один для подсчета числа студентов, для подсчета числа студентов, его не сдав¬ ших. 4. После того, как программа шить, сдали ли Давайте перейдем экзамен обработает все результаты, она должна ре¬ более 8 студентов. Мы к нисходящему пошаговому уточнению алгоритма. начинаем с представления на псевдокоде верхнего уровня: Проанализировать результаты вышена Повторим плата за экзамена и важно акцентировать внимание на том, что ностью представляет программу, однако, по уточнений, прежде преобразовать в программу сколько решить, должна ли быть по¬ курс чем псевдокод можно на верхний уровень пол¬ всей вероятности, потребуется будет естественным С. Нашим первым уточнением не¬ образом является
110 Глава 3 Инициализировать переменные Ввести десять экзаменационных оценок и подсчитать число сданных и несданных экзаменов Вывести краткую ли быть Хотя здесь сводку повышена результатов плата за экзамена и решить, должна курс мы также имеем полное представление всей программы, необходи¬ дальнейшее уточнение. Теперь мы введем конкретные переменные. Нам по¬ требуются счетчики для регистрации количества сданных и несданных мо экзаменов, счетчик для управления выполнением цикла и переменная для хра¬ нения пользовательского ввода. Инициализировать может Оператор псевдокода переменные быть уточнен следующим образом: Инициализировать нулем количество сданных экзаменов Инициализировать нулем количество несданных экзаменов Инициализировать счетчик студентов единицей Обратите внимание, что инициализируются мы. Оператор псевдокода только счетчики и итоговые сум¬ Ввести десять экзаменационных оценок и подсчитать число сданных и несданных экзаменов требует цикла Здесь известно для последовательного ввода результатов каждого экзамена. заранее, что имеется в точности десять результатов экзамена, подойдет цикл, управляемый счетчиком. Структура с двойным выбором внутри цикла (т.е. вложенная в цикл) будет определять для каждого следовательно, экзамена, является ли он в результате сданным или несданным, и исходя из этого увеличивать соответствующий счетчик. Уточнением предыдущего опера¬ тора псевдокода тогда будет Пока студентов меньше или равен следующий результат экзамена счетчик Ввести студент сдал экзамен Прибавить единицу к счетчику десяти Если сданных экзаменов иначе Прибавить единицу Прибавить единицу Обратите if/else, к к счетчику несданных счетчику экзаменов студентов управляющей удобочитаемой. Оператор псев¬ внимание на вставку пустых строк для выделения структуры делающих программу более докода Вывести краткую сводку результатов экзамена ли быть повышена плата за курс может быть уточнен следующим образом: Вывести количество сданных экзаменов Вывести количество несданных экзаменов сдали более восьми студентов Вывести «Повысить плату за курс» Если экзамен и решить, должна
111 Структурная разработка программ Полностью второе уточнение на показано рис. что и в данном случае для выделения структуры ки, что повышает удобочитаемость 3.9. Обратите внимание, while вставлены пустые стро¬ программы. Инициализировать нулем количество сданных экзаменов Инициализировать нулем количество несданных экзаменов Инициализируйте счетчик студентов единицей Пока студентов меньше или равен следующий результат экзамена счетчик Ввести студент сдал экзамен Прибавить единицу к счетчику десяти Если сданных экзаменов иначе Прибавить единицу Прибавить единицу Вывести количество к к счетчику несданных счетчику сданных экзаменов студентов экзаменов несданных экзаменов Если экзамен сдали более восьми студентов Вывести «Повысить плату за курс» Вывести количество Рис. 3.9. Псевдокод для задачи псевдокод является достаточно детализированным для Теперь С. Программа 3.10. Обратите внимание, вания его в код рис. о результатах экзаменов зволяющим инициализацию инициализация происходит Совет по повышению на С преобразо¬ и два примера ее выполнения показаны на что мы воспользовались объединять с свойством языка С, объявлениями. Подобного по¬ рода во время компиляции. эффективности 3.1 Инициализация переменных при их объявлении уменьшает время выполнения про¬ граммы Общее методическое замечание 3.6 Опыт показывает, что наиболее трудной частью решения задачи на компьютере явля¬ алгоритма ее решения Как только соответствующий алгоритм по¬ строен, процесс написания работающей программы на С обычно не вызывает затруд¬ нений ется разработка Общее методическое замечание 3.7 Многие программисты пишут программы, никогда не пользуясь никакими инстру¬ разработки программ типа псевдокода Они полагают, что поскольку их ко¬ ментами нечной целью является решение задачи на компьютере, написание псевдокода только задерживает производство конечного продукта
112 Глава 3 /* Анализ результатов экзамена */ #include <stdio.h> mam () { /* инициализация переменных при их объявлении */ int passes 0, failures 0, student 1, result; = = = /* обрабатывает 10 студентов; цикл, управляемый while (student <= 10) { printf("Enter result (l=pass, 2=fail): "); scanf("%d", if (result passes счетчиком */ &result); == = /* 1) + passes if/else вложенная в while */ 1; else failures student = = + failures student + 1; 1; } printf("Passed %d\n", passes); pnntf("Failed %d\n", failures); if (passes > 8) printf("Raise return 0; /* tuition\n"); успешное завершение */ } Enter result l=pass, 2=fail) 1 Enter result l=pass, 2=fail) 2 Enter result l=pass, 2=fail) 2 Enter result l=pass, 2=fail) 1 Enter result l=pass, 2=fail) 1 Enter result l=pass, 2=fail) 1 Enter result l=pass, 2=fail) 2 Enter result l=pass, 2=fail) 1 Enter result l=pass, 2=fail) 1 Enter result l=pass, 2=fail) 2 Passed 6 Failed 4 Enter result l=pass, 2=fail) 1 Enter result l=pass, 2=fail) 1 Enter result l=pass, 2=fail) 1 Enter result l=pass, 2=fail) Enter result l=pass, 2=fail) 1 Enter result l=pass, 2=fail) 1 Enter result l=pass, 2=fail) 1 Enter result l=pass, 2=fail) 1 Enter result l=pass, 2=fail) 1 Enter result l=pass, 2=fail) 1 Passed Failed Raise Рис. 3.10. 9 1 tuition Программа на С и пример ее выполнения для задачи о результатах экзаменов
113 Структурная разработка программ Операции 3.11. В языке роткой с может с С предусмотрено несколько операций выражений. Например, оператор записи = + с присваивания для более ко¬ 3 ; быть сокращен += присваивания с помощью операции сложения с присваиванием += до 3; Операция += прибавляет значение выражения справа от операции к зна¬ переменной слева от нее и сохраняет результат в переменной слева от чению Любой оператор вида операции. = переменная переменная операция выражение; где операция является гих, которые мы одной обсудим операций +, из двухместных в главе 10), может быть *, / -, или % (дру¬ записан в виде переменная операция= выражение; Таким образом, присваивание с += 3 прибавляет 3 к значению перемен¬ ной с. На рис. 3.11 показаны арифметические операции присваивания, приме¬ в которых они используются, ры выражений, Операция при¬ Пример выра¬ сваивания жения int Предположим += -= * = / с с += 7 d -= *= f g e = %= d 3, = iв = 4, с с 4 d = d 5 e = e /= 3 f = f % 9 g = g = по повышению Выражение 5, Присваивает Пояснение = Рис. 3.11. Совет = f = 6, 12; переменной с 4 1 переменной d 5 20 / 3 2 переменной f % 9 3 переменной g * переменной e Арифметические операции присваивания эффективности 3.2 операцией присваивания (например, с += 3) компилируется быстрее, с + 3), поскольку переменная с в пер¬ раскрытое выражение (с с = выражении оценивается только один раз, она оценивается дважды по повышению из := 10 - вом Многие g 7 + чем эквивалентное Совет и соответствующие пояснения. советов по в то время как во втором выражении эффективности 3.3 эффективности, которые мы даем в этой первый взгляд улучшениям, поэтому у читателя повышению приводят к незначительным на книге, может возникнуть искушение игнорировать их Дело в том, что заставить программу выпол¬ няться значительно быстрее может только совокупный эффект от всех этих мер по по¬ вышению сении того, значительное улучшение достигается при вне¬ незначительного усовершенствования в цикл, который может эффективности Кроме вроде повторяться бы большое количество раз.
114 Глава 3 3.12. В языке Операции инкремента С предусмотрены и декремента также унарная операция инкремента ++ и уна¬ краткая информация о которых приведена на операция декремента с + 1 или рис. 3.12. При увеличении переменной с на 1 вместо выражений с --, рная = 1 с += может использоваться операция инкремента ++. Если операции инкре¬ переменной, они называются соот¬ ветственно преинкрементными или предекрементными операциями. Если операции инкремента или декремента помещаются после переменной, они на¬ зываются соответственно постинкрементными или постдекрементными операциями. Операция преинкремента (предекремента) над переменной вызы¬ вает увеличение (уменьшение) этой переменной на 1, затем новое значение пе¬ ременной используется в выражении, в котором она появляется. Операция по¬ стинкремента (постдекремента) над переменной вызывает использование те¬ кущего значения этой переменной в выражении, вкотором она появляется, за¬ тем значение переменной увеличивается (уменьшается) на 1. мента или декремента помещаются перед Операция Пример выражения Пояснение ++ ++а Увеличивает а в ++ а на выражении, в которое входит а, значение а в выражении, затем увеличивает а на в 1 Уменьшает b на 1, затем использует новое значение b в выражении, в которое входит b --b b-- -- затем использует новое значение которое входит а Использует текущее а++ -- 1, Использует текущее которое Рис. 3.12. входит b, Операции инкремента значение b затем в выражении, уменьшает b на в 1 и декремента В программе, приведенной на рис. 3.13, демонстрируется различие между преинкрементной и постинкрементной версиями операции ++. Постинкре¬ ментирование переменной с вызывает ее увеличение после ее использования в функции printf. Преинкрементирование переменной с вызывает ее увеличе¬ ние до ее использования в функции printf. Программа отображает значение переменной с до и после использования операции ++. Операция декремента ( ) действует аналогично. Хороший стиль программирования 3.10 Унарные операции следует разделительных Три помещать непосредственно рядом с их операндами без пробелов оператора присваивания на рис. 3.10 passes passes + 1; failures + = failures student = = student + 1; 1; могут быть записаны более кратко с использованием виде операций присваивания в
115 Структурная разработка программ /* Операции преинкремента и постинкремента */ <stdio.h> #include main () { int с; с 5; = pnntf("%d\n", pnntf("%d\n", с); с + + ); printf("%d\n\n", с /* постинкремент /* преинкремент */ с); 5; = printf("%d\n", с); printf("%d\n", ++с); printf("%d\n", с); return /* 0; успешное завершение */ */ } 5 5 6 5 6 6 Рис. 3.13. Демонстрация различия между операциями преинкремента 1; += passes failures student и постинкремента 1; += += 1; с использованием операций преинкремента в виде ++passes; ++failures; ++student; или с операциями постинкремента passes++; failures++; student++; Здесь обратить внимание на то, что при инкрементировании или переменной в операторе, в который входит только эта пе¬ ременная, преинкрементная и постинкрементная формы дают один и тот же эффект. Только при появлении переменной в контексте большего выражения преинкрементная и постинкрементная формы имеют различный смысл (это же верно и для операций предекремента и постдекремента). В качестве операнда операций инкремента или декремента может быть ис¬ пользовано только простое имя переменной. важно декрементировании Распространенная ошибка программирования 3.10 Попытка использовать операцию инкремента или декремента с выражением, отлич¬ ным от простого имени переменной, вроде ++(x + 1), является синтаксической ошиб¬ кой
116 Глава 3 Хороший стиль программирования 3.11 Стандартом ANSI в общем случае не специфицируется порядок, в котором будут оце¬ ниваться операнды той или иной операции (хотя в главе 4 мы увидим, что для неко¬ операций правила существуют исключения) Поэтому программист с операциями инкремента или декремента, в которых некоторая переменная подвергается их воздействию более одного раза. торых должен из избегать В таблице этого команд 3.14 на рис. показаны сверху вниз в порядке уме¬ Во втором столбце ньшения их приоритета. раций показаны приоритет и ассоциативность представ¬ операций. Операции ленных до сих пор описывается ассоциативность опе¬ Обратите внимание, что инкремента (++), декремента ( ), для каждого уровня приоритетов. операция (-) минус (?:), унарные операции и приведения типа, и операции присваивания =, +=, -=, * условная плюс =, /= и (+), % = ассоциированы справа налево. В третьем столбце указаны названия различ¬ ных групп операций. Все другие операции на рис. 3.14 ассоциированы слева направо. Операции 0 ++ -- (тип) % / + - V V ! + - II Л Л II = 9 ; + = -= *= / = % = Рис. 3.14. Приоритет Ассоциативность Тип слева круглые скобки направо справа налево унарные слева направо мультипликативные слева направо аддитивные слева направо отношения слева направо равенства справа налево условные справа налево присваивания операций, упомянутых в книге до настоящего момента Резюме Решение любой задачи, связанной выполнение ряда действий в с вычислениями, определенном порядке. включает Процедура в себя реше¬ ния задачи в виде действий, которые надлежит выполнить, и порядка, в котором эти действия должны быть выполнены, называется алгорит¬ мом. в котором в компьютерной программе должны вы¬ полняться операторы, называется программным управлением. Задание порядка, Псевдокод искусственный неформальный язык, который помога¬ разрабатывать алгоритмы. Он напоминает повседнев¬ ный язык. Программы на псевдокоде на самом деле не выполняются на компьютерах. Скорее они просто помогают программисту «продумы¬ это ет программистам вать» программу перед ния, таком, например, попыткой как С. написать ее на языке программирова¬
117 Структурная разработка программ Псевдокод состоит исключительно из символов, поэтому программисты могут вводить программы на псевдокоде в компьютер, редактировать и сохранять их. Псевдокод состоит только из исполняемых операторов. Объявления представляют собой сообщения компилятору, которые передают ему ат¬ переменных и дают ему указание зарезервировать для них мес¬ то в памяти. рибуты Структура выбора используется ных направлений действия. для избрания одного из альтернатив¬ В структуре выбора if указанное действие когда условие выполняется только тогда, истинно. указывает, что в зависимости от истинности условия должны выполняться различные действия. Структура выбора if/else или ложности Во вложенной структуре выбора if/else может проверяться несколько различных случаев. Если истинным оказывается более одного условия, выполнены только операторы после первого истинного условия. будут Всякий раз, когда должно быть выполнено более одного оператора в том месте программы, где обычно ожидается появление только одного быть заключены в фигурные скобки, образующие составной оператор. Составной оператор может быть поме¬ щен в любое место программы, где может стоять простой оператор. оператора, эти операторы должны пустого оператора, показывающего, что не должно выпол¬ няться никакое действие, служит точка с запятой (;) на месте, обычно Признаком занимаемом некоторым оператором. повторения требует многократного выполнения действия до тех пор, пока некоторое условие остается истинным. Структура структуры повторения while: Формат while (условие) оператор также составной оператор или блок), содержащийся структуре повторения while, составляет тело этой структуры. Оператор (а Обычно некоторое действие, выполняемое в теле структуры while, в дол¬ жно в конечном итоге приводить к тому, что условие становится лож¬ ным. В противном случае цикл никогда не будет завершен возникает ошибка, «бесконечным циклом». называемая В цикле, управляемом счетчиком, для определения момента заверше¬ ния счетчик проходов цикла используется некоторая переменная цикла. Итоговая сумма представляет собой переменную, в которой накаплива¬ ется сумма последовательности чисел. Итоговые суммы обычно должны быть инициализированы нулем до Блок-схема совании является цикла. графическим представлением алгоритма. При ри¬ блок-схем используются некоторые специальные символы, кие как овалы, прямоугольники, ками, выполнения называемыми линиями ромбы та¬ и кружки, соединяемые стрел¬ перехода. Символы указывают на
Глава 3 118 действия, которые подлежат выполнению. Линии перехода указывают на порядок, в котором должны выполняться действия. Символ овала, называемый начало и всякого конец также конечным символом, указывает на алгоритма. Символ прямоугольника, называемый также символом действия, ука¬ зывает на вычисления любого типа или операцию ввода/вывода. Сим¬ волы соответствуют прямоугольников полняются с помощью операторов действиям, которые обычно присваивания, или вы¬ операторам ввода/вывода, которые реализуются при посредстве стандартных биб¬ лиотечных функций, подобных printf и scanf. Символ ромба, зывает также называемый жит выражение, которое может исходят две линии перехода. ление, символом принятия решения, ука¬ необходимость выбора. Символ принятия решения содер¬ на которое показывает должно быть Одна истинным или ложным. быть выбрано, направление, Из него линия перехода показывает направ¬ выбираемое если в условие том истинно; случае, другая когда условие ложно. Значение, которое содержит дробную часть, называется вающей точкой и представляется типом данных float. числом с пла¬ Деление двух целых чисел приводит к целочисленному делению, при котором дробная часть вычисления теряется (т.е. происходит усечение числа). В языке С предусмотрена одноместная операция приведения типа (flo¬ которая создает для своего операнда временную копию с плаваю¬ щей точкой. Использование операции приведения типа подобным обра¬ зом называется явным преобразованием. Операции приведения типа at), существуют для любого типа данных. Компилятор С умеет оценивать лишь выражения с идентичными ти¬ пами данных операндов. Чтобы гарантировать совпадение типа опе¬ рандов, компилятор выполняет над выбранными операндами так на¬ зываемую операцию возведения типа Стандартом ANSI специфицируется, (или неявное преобразование). int дела¬ float. Стандарт ANSI преду¬ что для операндов типа ются копии, которые возводятся до типа сматривает набор правил возведения для операндов различных ти¬ пов. плавающей точкой выводятся с определенным числом зна¬ десятичной точки; оно задается спецификатором преобразо¬ вания %f в операторе printf. Значение 3.456, выведенное со специфи¬ катором преобразования %.2f, отображается на экране как 3.46. Если Значения с ков после используется спецификатор преобразования %f (без указания сти), то по умолчанию принимается точность, равная 6. В языке точно¬ С предусмотрены разнообразные операции присваивания, по¬ записывать более коротко некоторые распространенные могающие типы вы: арифметических выражений присваивания. Эти операции +=, -=, *=, /= переменная = и %=. Вообще любой оператор вида переменная операция выражение; тако¬
119 Структурная разработка программ где операция является быть может записан переменная одной в из двухместных операций +, операция= или % и операция декре¬ переменной на 1. переменным в префиксной или по¬ используется в префиксной форме, для увеличения или уменьшения значения мента Эти операции могут применяться стфиксной формах. Если переменная сначала к операция увеличивается или уменьшается на 1 и затем Если операция применяется в постфик¬ в оценке выражения. участвует сной форме, или *, / выражение; В языке С предусмотрена операция инкремента ++ то -, виде то переменная участвует в на уменьшается оценке и затем увеличивается 1. Терминология float первое уточнение алгоритм арифметические операции присваивания: +=, -=, *=, /= бесконечный цикл передача управления повторение и % = блок повторение, управляемое счетчиком порядок действий блок-схема последовательная структура последовательное выполнение вложенные структуры if/else вложенные управляющие пошаговое уточнение пробельные символы структуры возведение псевдокод второе уточнение решение сигнальное значение пустой оператор (;) выбор действие деление символ действия на символ завершения символ овала ноль инициализация итоговая «конец сумма ввода конечный данных» символ контрольное крах символ прямоугольника символ решения goto исключение символы блок-схемы значение синтаксическая ошибка программы линии составной оператор перехода логическая ошибка мультипликативные выбора if структура выбора if/else структура повторения while структура операции «мусор» неопределенное символ ромба символ стрелки повторение нефатальная ошибка неявное преобразование структура с двойным выбором структура с единичным выбором структура со множественным выбором нисходящее пошаговое уточнение структурное программирование округление структуры выбора структуры повторения оператор goto операция декремента операция инкремента ( ) (++) суперпозиция управляющих структур счетчик операция постдекремента тело цикла операция постинкремента операция предекремента тернарная точность операция преинкремента точность по умолчанию операция приведения управление программой определенное повторение отказ программы управляющая структура усечение (трехместная) операция
120 Глава 4 управляющие структуры с одним входом / одним выходом фиктивное значение флаговое значение условие завершения целочисленное деление условная операция (?:) цикл число с плавающей точкой шаг фаза завершения фаза инициализации явное преобразование фаза обработки фатальная ошибка Распространенные ошибки программирования 3.1. Пропуск одной или обеих фигурных скобок, ограничивающих ставной оператор. 3.2. Помещение точки с запятой после условия в условной структуре приводит к логической ошибке в структурах if с одним выбором и к синтаксической ошибке 3.3. Отсутствие в теле в структурах if двойным выбором. с while действия, которое структуры со¬ в конечном приводит к тому, что условие в структуре while становится ложным. Обычно такая структура повторения никогда не будет за¬ итоге 3.4. ошибка, вершена возникает Написание ключевого While (не забывайте, различающим слова что С называемая while с W является в «бесконечным циклом». верхнем языком регистре в виде программирования, регистр). Все зарезервированные ключевые слова while, if и else, содержат только символы ниж¬ языка С, такие как него 3.5. Если ты регистра. счетчик или итоговая сумма не инициализированы, работы вашей Это ными. программы является примером по 3.6. Выбор контрольного такого допустимым значением данных. 3.7. Попытка деления на ноль, являющаяся в результа¬ всей вероятности, некоррект¬ логической ошибки. будут, качестве значения, которое является причиной фатальной ошиб¬ ки. 3.8. Указание точности для спецификации преобразования в строке управления форматом функции scanf является ошибочным. Точ¬ ность используется только в спецификациях преобразования функ¬ ции printf. 3.9. Предположение, что числа с плавающей точкой будут представлены абсолютной точностью, может привести к не¬ На большинстве компьютеров числа с результатам. в памяти компьютера с правильным плавающей точкой представляются З.Ю.Попытка Хороший 3.1. 4- приблизительно. использовать операцию инкремента или декремента с вы¬ ражением, 4-+(x только 1), стиль отличным является от простого имени переменной, вроде синтаксической ошибкой. программирования Единообразное применение разумных соглашений об отступах чительно повышает удобочитаемость зна¬ программы. Мы предлагаем ис¬
121 Структурная разработка программ пользовать для отступа 1/4 дюйма в зительно 3.2. Псевдокод часто табуляцию фиксированного или три пробела. используется процессе ее проектирования. в разуется программу на для размера «продумывания» Затем программа прибли¬ программы на псевдокоде в преоб¬ С. 3.3. Делайте отступы для обеих групп операторов структуры if/else. 3.4. нескольких уровней отступов отступы для каждого должны уровня содержать одно и то же количество дополнительных При наличии пробелов. 3.5. Некоторые программисты предпочитают вводить начальную и завер¬ шающую фигурные скобки в составных операторах до начала ввода операторов внутри фигурных скобок. Это помогает избежать пропус¬ ков одной или обеих фигурных скобок. 3.6. Инициализируйте 3.7. При выполнении деления на выражение, значение которого может быть нулем, явно осуществляйте проверку этого случая и обраба¬ тывайте щение его об и счетчики итоговые суммы. образом (например, выводите сооб¬ допускайте возникновения фатальной соответствующим ошибке), и не ошибки. 3.8. В цикле, управляемом контрольным значением, подсказки для за¬ проса ввода данных должны явно напоминать пользователю, чему равно это значение. 3.9. Не проверяйте на равенство З.Ю.Унарные операции следует значения с плавающей точкой. помещать непосредственно рядом с их операндами без разделительных пробелов. З.Н.Стандартом ANSI в общем случае не специфицируется порядок, в котором будут оцениваться операнды той или иной операции (хотя в главе 4 мы увидим, что для некоторых операций из этого правила существуют исключения). Поэтому программист должен избегать команд с операциями инкремента или декремента, торых некоторая переменная подвергается Советы по повышению 3.2. ко¬ эффективности Инициализация переменных при выполнения в воздействию более * одного раза. 3.1. их их объявлении уменьшает время программы. операцией присваивания (например, с += 3) компили¬ с + 3), быстрее, чем эквивалентное раскрытое выражение (с Выражение с = руется поскольку переменная с один раз, в то время в как первом во выражении втором оценивается только выражении она оценивается дважды. 3.3. Многие из советов по повышению эффективности, которые мы даем в этой книге, приводят к незначительным на первый взгляд улучше¬ ниям, поэтому у читателя может возникнуть искушение игнориро¬
122 Глава 3 вать их. льно Дело в том, быстрее может повышению что заставить программу выполняться значите¬ совокупный эффект от всех этих мер по эффективности. Кроме того, значительное улучшение только достигается при внесении вроде бы незначительного усовершенство¬ вания в цикл, который может повторяться большое количество раз. Общие 3.1. методические замечания Составной оператор может быть помещен где 3.2. может простой стоять любое место программы, Подобно тому, как составной оператор можно помещать всюду, где может быть помещен простой оператор, так же возможно не поме¬ щать там никакого оператора Пустой оператор. пятой (;) 3.3. в оператор. на вообще, то есть использовать пустой оператор представляется помещением точки с за¬ место, обычно занимаемое некоторым оператором. так же как и сам верхний уровень, представляет собой полную спецификацию алгоритма; меняется только уровень Каждое уточнение, детализации. 3.4. Многие программы логически могут быть разделены на три этапа: этап инициализации, на котором происходит инициализация пере¬ менных программы; и данных этап завершения ление 3.5. и этап обработки, на котором происходит ввод настройка переменных программы; и соответствующая вывод работы программы, на котором происходит вычис¬ окончательных результатов. Программист завершает процесс нисходящего пошагового уточне¬ специфицирован до¬ того, чтобы программист мог преобразовать на С. Реализация программы на С в этом слу¬ ния в тот момент, когда алгоритм на псевдокоде статочно подробно для псевдокод в программу чае обычно 3.6. Опыт не вызывает затруднений. показывает, что наиболее трудной частью решения задачи на разработка алгоритма ее решения. Как только соответствующий алгоритм построен, процесс написания работаю¬ щей программы на С обычно не вызывает затруднений. компьютере является 3.7. Многие программисты пишут программы, никогда не пользуясь ни¬ какими инструментами разработки программ типа псевдокода. Они полагают, что поскольку их конечной целью является решение зада¬ чи на компьютере, написание псевдокода только задерживает произ¬ водство конечного продукта. Упражнения 3.1. для самоконтроля Ответьте на каждый из следующих вопросов. в a) Процедура решения задачи виде выполнить, и порядка, в котором эти нены, называется действий, которые надлежит действия должны быть выпол¬ . b) Задание зывается порядка, в котором компьютер выполняет операторы, на¬ .
123 Структурная разработка программ c) Все программы могут быть написаны управляющих структур: с использованием и , трех . d) Структура выбора используется для выполнения не¬ которого действия в случае истинности условия и для выполнения другого действия в случае его ложности. e) Несколько операторов, сгруппированных вместе в фигурных скоб¬ ках называются ({ }), и . f) Структура повторения специфицирует многократное выполнение оператора или группы операторов, пока некоторое усло¬ вие остается истинным. g) Повторение набора команд определенное число раз называется по¬ вторением, . h) Если заранее неизвестно, сколько раз будет повторено некоторый набор операторов, то для завершения повторения может быть испо¬ льзовано значение. 3.2. Напишите четыре различных оператора С, каждый из которых при¬ бавляет 1 к целочисленной переменной x. 3.3. Напишите одиночный оператор С для ющих выполнения каждой из следу¬ задач: a) Присвойте сумму x и у менной 1 x на b) Умножьте после переменной проведения z и увеличьте значение переменной product значение пере¬ вычисления. на 2, используя опера¬ цию *=. c) Умножьте = и переменной product значение на 2, используя операции *. d) Проверьте, является ли значение переменной count больше 10. Если является, выведите на экран «Значение переменной count бо¬ льше 10». e) Уменьшите значение переменной x на 1, затем вычтите его из пе¬ ременной total. f) Прибавьте ньшите x на g) Вычислите значение переменной x к переменной total, затем уме¬ 1. переменной q на значение перемен¬ результат переменной q. Напишите этот опе¬ ратор двумя различными способами. ной divisor и остаток от деления присвойте h) Выведите значение 123.4567 с точностью до 2 знаков. Какое зна¬ будет выведено? i) Выведите значение с плавающей точкой, равное 3.14159, с тремя знаками после десятичной точки. Какое значение будет выведено? чение 3.4. Напишите оператор С для выполнения каждой из следующих задач. a) Объявите переменные sum и x с b) Инициализируйте переменную x c) Инициализируйте типом int. единицей. переменную sum нулем. d) Прибавьте переменную тат переменной sum. x к переменной sum и присвойте резуль¬
124 Глава 3 e) Выведите «Сумма равна: на экран строку », за которой следует значением переменной sum. 3.5. Объедините операторы, которые вы написали в упражнении 3.4, в программу, которая вычисляет сумму целых чисел от 1 до 10. Испо¬ льзуйте структуру while для выполнения ления и инкрементных значение 3.6. переменной Определите станет каждой числения. Предположите, оператора все b) result 11. перед равны началом выполнения каждого 5. x+ + ; + ++x = равным в цикле указанного вычис¬ должен завершиться, когда из переменных после выполнения вы¬ что переменные *= a) product 3.7. значение x операций. Цикл x; Напишите одиночный оператор С, который a) Вводит целочисленную переменную x с b) Вводит целочисленную переменную у с помощью scanf. помощью scanf. c) Инициализирует целочисленную переменную i единицей. d) Инициализирует целочисленную переменную power единицей. e) Умножает переменную power на x и присваивает результат пере¬ менной power. f) Увеличивает переменную у на 1. g) Проверяет переменную шей или равной x. h) Выводит у, чтобы выяснить, является ли она мень¬ целочисленную переменную power с помощью printf. 3.8. Напишите ния программу на С, которая использует операторы упражне¬ 3.7 для расчета значения x в степени у. Программа должна включать управляющую структуру повторения while. 3.9. Найдите a) и while исправьте (с <= 5) *= product ошибки в каждом из следующих случаев: { с; ++с; b) scanf("%.4f", &value); c) if 1) == (gender printf("Woman\n"); else; printf("Man\n") ; З.Ю.Что неправильно в while sum (z += >= следующей структуре повторения while? 0) z; Ответы на упражнения для самоконтроля 3.1. а) Алгоритмом, b) Программным управлением, с) Последователь¬ ность, выбор, повторение, d) if/else. e) Составным оператором, f) while. g) Управляемым счетчиком, h) Контрольное.
125 Структурная разработка программ 3.2. x = x += 1; + x 1 ; ++x; x++; 3.3. a) + x++ = z у; b) product *= c) product = d) if > e) -= f) total += g) q %= = 2; 10) ("Count total * product (count printf q 2; is than greater 10.\n"); --x; x ; divisor; q %= divisor; h) printf("%.2f", 123.4567); Наэкранвыводится 123.46. i) 3.14159); ("%.3f\n", printf На экран выводится 3.142. 3.4. a) int b) x = sum, x; 1; c) sum = d) sum += 0; or x; e) printf("The 3.5. = sum sum is : sum x; %d\n", /* Вычисляет сумму целых #include <stdio.h> mam + sum); чисел от 1 () { int x = sum x; sum, 1; 0; = while <= (x sum += 10) { x; ++x; } printf ("The sum is: } 3.6. b) 3.7. result 25, = a) product = 12, a) scanf("%d", = x x = &x); b) scanf("%d", &y); c) i d) power = 1; = 1; 6; б; %d\n", sUm); до 10 */
126 Глава 3 e) power f) у++; g) if(у *= <= x) h) printf 3.8. x; ("%d", power); /* возводит x в степень #include <stdio.h> */ у main() int x,y,i,power; i 1; = 1; power scanf("%d", &x); scanf(%"d", &у); = while <= (i ++ { у) *= power x; i; printf return ("%d",power); 0; } 3.9. а) Ошибка: отсутствие структуры while. завершающей правой фигурной скобки в теле Исправление: добавьте завершающую правую фигурную скобку сле оператора ++c;. b) Ошибка: точность, используемая в по¬ спецификации преобразования функции scanf. Исправление: удалите .4 из спецификации преобразования. c) Ошибка: помещение точки с запятой после ветви else структуры if/else приводит выполняться Исправление: З.Ю.Значение к логической ошибке. Второй оператор printf будет всегда. удалите точку с запятой после else. переменной Следовательно, z ни возникает разу не изменяется в структуре while. бесконечный цикл. Для недопущения бес¬ конечного цикла переменная z должна уменьшаться, так чтобы в ко¬ нечном итоге ее значение стало равно 0. Упражнения З.Н.Найдите мечание: и в исправьте каждом ошибки в фрагменте каждом из следующих случаев кода может быть более ки): а) if (age >= 65); printf else ("Аде is greater pnntf ("Age is less than than or equal 65\n") ; to (За¬ одной ошиб¬ 65\n");
127 Структурная разработка программ b) int x 1, total; while (x <=10) { total += x; = ++x; } c) (x <= 100) total =+ x; While ++x; d) while > (у 0) { printf("%d\n" у) , ++у; } 3.12.3аполните пропуски a) Решение любой в следующих включает задачи определенном b) из каждом утверждений: выполнение действий ряда в . Синонимом для процедуры c) Переменная, в которой называется является накапливается . сумма нескольких чисел, . d) Процедура присвоения некоторым переменным значений в начале программы называется e) Специальное определенных . значение, используемое для указания на «конец вво¬ да данных», называется , или , значением. f) графическим представлением алгоритма. является g) В блок-схеме порядок, обозначается ритма, h) Конечный в котором должны выполняться шаги алго¬ символами символ указывает на . любого и алгоритма. i) Символы прямоугольников соответствуют вычислениям, которые обычно выполняются с помощью операторов операци¬ , ям ввода/вывода, стандартным которые обычно выполняются путем библиотечным функциям j) Элемент, который . 3.13.Что выводит следующая программа? () { int у 1, = x while (x = x <= * total 10) = 0, у; { x ; printf( "%d\n", total += у; у); ++x; } printf return ("Total 0; к . помещается внутри символа принятия реше¬ ния, называется main и и обращений is %d\n", total);
128 Глава 3 3.14. Напишите одиночные операторы псевдокода, которые соответствуют каждому из следующих действий: a) Отобразите b) Присвойте на экране сообщение «Введите сумму переменных x, у и z два числа». перем.енной р. c) Следующее условие должно быть проверено в структуре выбора if/else: текущее значение переменной m больше удвоенного текуще¬ го значения переменной v. d) Введите значения 3.15.Сформулируйте для переменных r s, и t с клавиатуры. алгоритм на псевдокоде для каждого из следующих действий: a) Введите те с клавиатуры два числа, вычислите их сумму и b) Введите какое не, отобразите с клавиатуры два числа, определите и (если какое-то) c) Введите отобрази¬ вычислений. результат этих из двух чисел на экра¬ больше. с клавиатуры последовательность положительных чисел, определите и отобразите на экране сумму этих чисел. Предположи¬ те, что пользователь вводит контрольное значение -1 для указания на «конец ввода данных». 3.16.Установите, какие из следующих ми и какие ясните b) показывает, что наиболее компьютере В рое высказываний Если утверждение являются истинны¬ объ¬ является ложным, почему. a) Опыт на ложными. является создание трудной частью решения задачи работающей программы на С. качестве контрольного должно использоваться значение, нельзя c) Линии спутать с допустимым значением кото¬ данных. перехода указывают на действия, подлежащие выполне¬ нию. d) Условия, гда записываемые внутри символов принятия решения, все¬ содержат арифметические операции (т.е. +, -, *, / и %). нисходящем пошаговом уточнении каждое уточнение являет¬ ся полным представлением алгоритма. e) При Для упражнений с 3.17 по 3.21 выполните каждый из следующих шагов: 1. Прочитайте постановку задачи. 2. Сформулируйте алгоритм, используя для нисходящее пошаговое 3. Напишите программу этого псевдокод на 4. Протестируйте, отладьте С. и выполните программу на С. 3.17.Из-за высокой цены на бензин водители интересуются их автомобилей. Некий водитель отследил го автомобиля, записывая каждой заправки и уточнение. пробегом пройденное расстояние в милях Разработайте программу на С в галлонах. сво¬ несколько заправок свое¬ и объем для вво¬ пройденного расстояния в милях и объема каждой заправки в Программа должна вычислять и отображать на экране ко¬ личество пройденных миль на галлон для каждой заправки автомо¬ да галлонах. биля. После обработки всей входной информациипрограмма должна
129 Структурная разработка программ вычислить и вывести по всем общее пройденных миль на галлон заправкам. Введите расход бензина Введите пройденный Для количество этой заправки (-1, путь: если ввод закончен): 12.8 287 получено миль/галлон 22.421875 Введите расход бензина (-1, если ввод закончен): 10.3 Введите пройденный путь: 200 Для этой заправки получено миль/галлон 19.417475 Введите расход бензина пройденный введите Для этой заправки если ввод закончен): 5 120 получено Введите расход бензина миль/галлон 24.000000 (-1, если ввод закончен): -1 миль/галлон 21.601423 число Среднее (-1, путь: 3.18. Разработайте программу на С, которая будет определять, превысил ли тот или иной покупатель универмага предельный размер кредита на своем расчетном счете. 1. Номер на начало 3. Общее сумма месяца по всем статьям расхода для данного покупателя в месяце 4. Общее сумма теля каждого покупателя доступна следу¬ счета 2. Баланс этом Для информация: ющая в этом всех кредитов, отнесенный на счет данного покупа¬ месяце 5. Допустимый предельный размер кредита Программа должна вводить все эти данные, вычислять новый баланс кредит) и определять, превышает (= начальный баланс + расходы - предельный размер кредита покупателя. Для тех покупателей, чей предельный размер кредита превышен, программа должна отображать на экране номер счета покупателя, предельный размер кредита, новый баланс и сообщение «Предельный размер ли новый баланс кредита превышен». счета (-1, если ввод закончен): Введите начальный баланс: 5394.78 Введите общую сумму расходов: 1000.00 Введите номер 500.00 Введите общую Введите предельный размер кредита: Счет: сумму кредита: 100 5500.00 100 Предельный размер кредита: Баланс: 5500.00 5894.78 Предельный размер кредита превышен. Введите номер счета (-1, если ввод закончен): Введите начальный баланс: 1000.00 Введите общую сумму расходов: 123.45 Введите общую сумму кредита: 321.00 Введите 5 Зак 801 предельный размер кредита: 1500.00 200
Глава 3 130 300 счета (-1, если ввод закончен): Введите начальный баланс: 500.00 Введите общую сумму расходов: 274.73 Введите номер 100.00 Введите общую Введите предельный размер кредита: сумму Введите номер кредита: (-1, счета 3.19.Некая крупная химическая основе комиссионных неделю плюс если 80 -1 закончен): ввод компания платит своим вознаграждений. Продавцы 9 процентов продавцам получают на $200 от их валовых продаж за эту неделю. в На¬ пример, продавец, реализующий за неделю химических препаратов на $5000, получает $200 плюс 9 процентов от $5000, или в сумме $650. Разработайте программу на С для ввода валовых продаж для за последнюю неделю и расчета и отображения на каждого продавца экране для заработка одного Введите этого продавца. Обрабатывайте за один раз данные продавца. сумму продаж закончен): в долларах (-1, если в долларах (-1, если ввод закончен): в долларах (-1, если ввод закончен): в долларах (-1, если ввод ввод 5000.00 $650.00 Зарплата: Введите сумму продаж 1234.56 $311.11 Зарплата: Введите сумму продаж 1088.89 $298.00 Зарплата: Введите 3.20.Простые interest сумму продаж -1 проценты по ссуде рассчитываются по формуле = principal * * rate days / В предыдущей формуле принимается, ставкой закончен): 365 что rate за год и поэтому включает деление на тайте программу является процентной 365 (дней). Разрабо¬ С, которая будет вводить значения principal, будет вычислять и отображать на экране простые проценты по каждой ссуде, используя для этого пре¬ дыдущую формулу. rate и days на для нескольких ссуд и Введите основную сумму ссуды (-1, Введите процентную ставку: .1 Введите срок в 365 Выплаты по Введите основную ссуды простым днях: процентам сумму (-1, .08375 если закончен): 1000.00 $100.00 ввод закончен): 1000.00 224 простым процентам составляют Выплаты по Введите основную сумму ссуды (-1, .0 9 процентную ставку: Введите ввод составляют ссуды Введите процентную ставку: Введите срок ссуды в днях: если Введите срок Выплаты по Введите основную сумму ссуды если ввод ссуды в днях: 1460 простым процентам составляют (-1, $51.40 закончен): 10000.00 $3600.00 если ввод закончен): -1
131 Структурная разработка программ программу на С для определения общей зарплаты каждого из нескольких служащих. Компания платит «обычную плату» за первые 40 часов, отработанных каждым служащим, и луторную зарплату» за все время, отработанное сверх 40 часов. 3.21.Разработайте предоставлены список служащих компании, число часов, ных каждым за последнюю служащим служащего его общую эту информацию и и неделю, каждого служащего. Ваша программа должна определить Вам отработан¬ почасовой тариф ввести для отобразить и для зар¬ «по¬ каждого на экране зарплату. Введите # отработанных часов (-1, Введите почасовой тариф работника Зарплата составляет $390.00 если Введите # отработанных часов (-1, Введите почасовой тариф работника Зарплата составляет $400.00 если Введите # отработанных часов (-1, Введите почасовой тариф работника Зарплата составляет $415.00 если Введите # отработанных если 3.22. Напишите программу часов на (-1, ввод ($00.00): ввод ($00.00): ввод ($00.00): ввод закончен): 10.00 39 закончен): 10.00 40 закончен): 10.00 41 закончен): -1 С, показывающую различие между опера¬ циями предекремента и постдекремента, используя для этого опера¬ цию декремента --. 3.23. Напишите программу 10, располагая их бок мя на о С, которая выводит в одной и той же бок в цикле числа от 1 до строке и разделдя тре¬ пробелами. 3.24.Процесс нахождения наибольшего числа (т.е. максимума группы чисел) часто используется в компьютерных приложениях. Напри¬ мер, программа для определения победителя конкурса продаж вво¬ дит количество единиц товара, проданных каждым продавцом. Про¬ реализовавший наибольшее количество единиц побеждает в конкурсе. Напишите программу на псевдокоде, давец, С для ввода серии наибольшего из этих программу на на печать должна counter; использовать счетчик до такие из 10 чисел. Подсказка: отслеживания number; текущее число, введенное С, которая таблицу значений: N 5* в 10*N 100*N 1000*N 1 10 100 1000 2 20 200 2000 3 30 300 3000 4 40 400 4000 количества обработаны все введен¬ 10 чисел). программу. наибольшее число, найденное до на ваша программа переменные: 10 (т.е. для 3.25. Напишите программу а затем чисел и определения и вывода ных чисел и определения момента, когда largest; товара, сих пор. выводит в цикле следующую
132 Глава 3 5 50 500 6 60 600 6000 7 70 700 7000 8 80 800 8000 9 90 900 9000 10 100 1000 10000 Для столбцов табуляциями табуляции \t. отделения использован 5000 3.26.Напишите программу таблицу значений: 3.27. printf может быть на С, которая порождает в цикле следующую А А+2 А+4 3 5 7 9 6 8 10 12 15 А+6 9 11 13 12 14 16 18 15 17 19 21 аналогичный Применяя подход, 3.24, найдите из 10 можете в операторе символ вводить чисел два каждое применявшемуся наибольших число только значения. один в упражнении Замечание: вы раз. 3.28. Измените программу на рис. 3.10 так, чтобы она проверяла правиль¬ ность своих входных данных. После ввода любого значения, отлич¬ ного от 1 или 2, продолжайте выполнение цикла до тех пор, пока по¬ льзователь не введет значения. правильного 3.29. Что выводит следующая программа? #include <stdio.h> main () { int count while 1; = <= (count 10) { count printf("%s\n", % 2 ? "****" :"++++++++"); ++count; } return 0; } 3.30. Что выводит следующая #include программа? <stdio.h> main () { int row while (row column while column; 10, = >= = 1) (column printf { 1; <= ("%s", ++column; 10) { row % 2 ? "<" : ">");
133 Структурная разработка программ } --row; printf"\n"); } return 0; } 3.31.(Проблема висящего else) Определите результаты вычислений для каждого из следующих фрагментов кода, когда x равен 9 и у равен 11, и когда x равен 11 и у равен 9. Обратите внимание, что компиля¬ тор игнорирует отступы в программе на С. Кроме того, компилятор if, если фигурных скобок {}. Поскольку на С всегда ассоциирует else с последним предшествующим ему не иначе указано первый посредством взгляд программист может и не быть уверенным, какому if соответствует данный else, льше усложнить b) if (x < if (у > (Подсказка: изучили.) «вися¬ чтобы еще бо¬ примените соглашения об от¬ 10) 10) printf else ("*****\n"); printf ("ff#ll\n"); pnntf ( "$$$$$\n" ) ; if (x < 10) if (у > 10) printf проблемы из следующего кода, задачу. ступах, которые вы a) это получило название else». Мы удалили отступы щего { ("*****\n"); } else { printf ("lfllf\n"); pnntf ("$$$$$\n") ; } 3.32.(Еще одна проблема висящего получения показанного вывода. else) Измените следующий код для Используйте соответствующие мето¬ ды выравнивания. Возможно, вам не придется делать никаких изме¬ нений, кроме вставки фигурных скобок. Компилятор игнорирует выравнивание в программах на С. Мы удалили из кода отступы, что¬ бы еще больше усложнить задачу. Замечание: возможно, не ется никаких if if (у (x printf else printf printf printf 8) 5) ("00000\n") == == ("#####\n"); ("$$$$$\п"); ("&&&&&\n"); а) В предположении, 00000 $$$$$ &&&&& потребу¬ изменений. что x = 5 и у = 8, программа должна вывести:
134 Глава 3 b) В предположении, что x = 5 и у предположении, что x = 5 и у = = 8, программа должна вывести: @@@@@ c) В 8, программа должна вывести: @@@@@ &&&&& В предположении, d) ющий ются что x = 5 и у = 7, должен быть получен следу¬ вывод. Замечание: все три последних оператора частью составного printf явля¬ оператора. ##### $$$$$ &&&&& 3.33. Напишите программу, которая и затем выводит этот квадрат должна работать для всех 20. Например, если ваша она должна считывает размер стороны квадрата в виде Ваша программа 1 и звездочек. квадратов с размерами сторон между считывает программа размер, равный 4, вывести **** **** **** **** 3.34. Измените программу, которую вы написали в упражнении 3.33 так, чтобы она выводила полый квадрат. Например, если ваша програм¬ ма считывает размер, равный 5, она должна вывести ***** * * * * * * ***** 3.35. Палиндромом называется число или фраза одинаково как слева направо, так и в текста, которая читается обратном порядке. Например, каждое из следующих пятизначных целых чисел является палинд¬ ромом: 12321, 55555, 45554 и 11611. Напишите программу, которая считывает пятизначное целое число и определяет, является ли оно примените операции деления и взятия по модулю для разложения числа на отдельные цифры.) палиндромом. (Подсказка: 3.36. Введите целое число, содержащее только 0 и 1 (т.е. «двоичное» це¬ лое число) и выведите его десятичный эквивалент. (Подсказка: при¬ мените операции деления и взятия по модулю для отделения справа налево одного за другим разрядов «двоичного» числа. Подобно тому, как в десятичной системе счисления цифра самого правого разряда имеет позиционное значение 1, следующая из оставшихся цифр име¬ ет позиционное значение 10, потом 100, потом 1000 и т.д., в системе двоичного счисления цифра самого правого разряда имеет позицион¬ ное значение 1, следующая из оставшихся цифр имеет позиционное значение 2, потом 4, потом 8 и т.д. Таким образом, десятичное число 234 может быть интерпретировано как 4 * 1 + 3 * 10 + 2 * 100. Деся¬ тичным эквивалентом двоичного + 1 * 8 или 1 + 0 + 4 + 8, 1101 или 13.) является 1 * 1 + 0 * 2 + 1 * 4
135 Структурная разработка программ 3.37. Мы все время слышим о том, как зом вы можете функционирует циклом определить, ваша собственная машина? Напишите программу с считает от 1 до 3 000 000 с шагом 1. Всякий while, который раз, когда результат это значение мое быстры компьютеры. Каким обра¬ быстро в действительности насколько для счета становится кратным на экран. выполнения По 1 000 000, выводите рассчитайте время, требуе¬ миллиона повторений цикла. своим часам каждого 3.38.Напишите программу, которая выводит 100 звездочек, по одной за один раз. После каждой десятой звездочки ваша программа должна выводить символ новой строки. Примените операцию чаев, счетчик когда становится 3.39. Напишите программу, которая (выводя результат на (Подсказка: считайте от 1 до 100. взятия по модулю для распознавания всех слу¬ печать), кратным 10. считывает целое число и определяет сколько цифр 3.40. Напишите программу, которая отображает в этом числе равно на экране 7. следующий ри¬ сунок шахматной доски: Ваша программа может использовать только три оператора printf, один в виде "* printf( Другой printf(" и "); "); третий printf ("\n"); 3.41. Напишите программу, которая последовательно выводит числа, кратные целому числу 2, а именно 2, 4, 8, 16, 32, 64 и т.д. Ваш цикл не должен завершаться (т. e. вы должны создать бесконечный цикл). Что произойдет, когда 3.42. Напишите и будете выполнять эту программу? программу, которая считывает радиус круга чения типа риметра вы float), (в виде зна¬ вычисляет и выводит значения его диаметра, пе¬ площади. Примите для n значение 3.14159. 3.43. Что неправильно в следующем операторе? Перепишите оператор, чтобы он выполнял то, что, возможно, хотел сделать программист. printf ("%d", ++(x + у)); 3.44. Напишите программу, которая считывает три ненулевых значения типа float и определяет, могут ли они представлять стороны треуго¬ льника, выводя результат на экран.
136 Глава 3 3.45. Напишите программу, которая считывает три ненулевых целых чис¬ ла и определяет, могут ли они быть сторонами прямоугольного треу¬ гольника, выводя результат на экран. 3.46. Компании необходимо передавать данные по телефону, но есть опа¬ сение, что телефоны могут прослушиваться. Все данные передаются в виде четырехзначных целых чисел. Представители просили вас шифровки сделать их написать программу для передачу шифровать разом: «Заменить каждую цифру значением по модулю 10. Затем ванное чтобы счи¬ его следующим (суммы об¬ этой цифры и цифру и третью, зашифрованное целое Напишите отдельную программу, которая вводит зашифро¬ четырехзначное целое число и дешифрует его, воссоздавая вторую цифру число. компании по¬ данных, более надежной. Ваша программа должна тывать четырехзначное целое число и 7) их и поменять местами первую четвертую». Затем вывести первоначальное число. 3.47. Факториал неотрицательного целого числа n записывается в виде n\ (произносится «п факториал») и определяется следующим образом: п1 п(п-1)(п-2)...(для значений n> больших или равных 1) = и n\ 1 (для ra=0). Например, 5! 5 = 4 = 3 2 1, что равняется a) Напишите программу, которая число, вычисляет и выводит его b) Напишите программу, которая ской константы e по формуле: 1 1 1 120. считывает неотрицательное целое факториал. оценивает значение математиче¬ + е_1+1! 2!+3!4'c) Напишите программу, x x2 x3 = 1+ + й ^ + ^Г+- которая вычисляет значение ех по формуле
г л а в а 4 Управление программой т Цели Научиться применению структур повторения for и do/while. Изучить структуру множественного выбора switch. Научиться применению операторов управления break и continue. Освоить использование логических операций.
138 Глава 4 Содержание 4.1. Введение 4.2. Основы структур повторения 4.3. Повторение, управляемое 4.4. Структура повторения for Структура for: замечания 4.5. 4.6. Примеры использования 4.7. Структура со Операторы break 4.10.Логические рекомендации структуры for и switch выбором do/while continue операции 4.11. Смешивание операций 4.12.Краткая сводка Резюме и множественным 4.8. Структура повторения 4.9. счетчиком по равенства (==) и присваивания (=) структурному программированию Распространенные ошибки программирования программирования Советы реносимости программ самоконтроля Ответы к упражнениям для самоконтроля 4.1. К настоящему моменту Хороший стиль эффективности Советы по пе¬ Общие методические замечания Упражнения для по повышению Упражнения Введение читатель должен чувствовать себя уверенно в на¬ С. В этой писании простых, но вместе с тем законченных программ на языке главе будет более подробно ставлены дополнительные структура for и структура ным рассмотрено повторение, в частности, управляющие do/while. Будет выбором switch. Мы обсудим оператор структуры будут повторения, а пред¬ именно введена структура со множествен¬ break для безотлагательного и бы¬ строго выхода из некоторых управляющих структур и оператор continue для пропуска дующей оставшихся операторов тела структуры повторения и перехода к сле¬ итерации цикла. В этой главе пользуемые для объединения условий, обсуждаются логические операции, ис¬ а заканчивается она кратким обзором принципов структурного программирования, представленных в главах 3 и 4.
139 Управление программой 4.2. Основы структур повторения Большинство программ содержит повторение, группа команд, выполняемых многократно условие продолжения способа повторений: т.е. циклы. компьютером, цикла остается истинным. До сих пор мы 1. Повторение, управляемое счетчиком 2. управляемое контрольным значением Повторение, Повторение, управляемое счетчикфм, иногда называют вторением, поскольку Цикл пока это некоторое обсудили два определенным по¬ будет выпол¬ мы в точности знаем заранее, сколько раз Повторение, управляемое контрольным значением, иногда называ¬ неопределенным повторением, поскольку заранее неизвестно, сколько раз нен цикл. ют потребуется выполнить цикл. В повторении, управляемом счетчиком, для подсчета числа повторений используется управляющая переменная. Управляющая переменная увеличи¬ вается (обычно на 1) всякий раз, когда выполняется тело цикла. Когда значе¬ ние управляющей переменной показывает, что было выполнено соответствую¬ повторений, цикл завершается, и компьютер продолжает выполне¬ программы с оператора, следующего за структурой повторения. щее число ние Контрольные значения используются для управления повторением в тех случаях, когда: 1. Заранее неизвестно точное число повторений, и 2. Цикл содержит операторы, которые получают данные при каждом полнении Контрольное после того, как Контрольные значение служит признаком в программу были переданы «конца данных». все обычные значения должны отличаться от возможных 4.3. вы¬ цикла. Повторение, управляемое Оно вводится элементы данных. значений данных. счетчиком Для повторения, управляемого счетчиком, требуется: 1. Имя управляющей переменной (или 2. Начальное значение счетчика цикла). управляющей переменной. 3. Значение инкремента (приращения со знаком плюс) или (приращения со знаком минус), на которое управляющая изменяет 4. Условие, свое значение после каждого выполнения декремента переменная цикла. в котором происходит проверка на конечное значение управ¬ ляющей переменной (т.е. должно ли продолжаться выполнение цикла). Рассмотрим простую программу, показанную на рис. 4.1, которая выводит числа от 1 до 10. Объявление int counter = 1; именует управляющую переменную (counter), объявляет зервирует для нее память и присваивает объявление ей ее целым числом, ре¬ начальное значение, равное не является исполняемым оператором. 1. Это
140 Глава 4 /* Повторение, управляемое счетчиком */ #include <stdio.h> main () int counter while 1; = (counter <= printf("%d\n", ++counter; return 10) { /* инициализация /* условие /* приращение */ повторения */ counter); */ 0; } 1 2 3 4 5 6 7 8 9 10 Рис. 4.1. Повторение, управляемое счетчиком Объявление и инициализация счетчика могли бы быть выполнены с помо¬ щью операторов int counter; counter 1; = Объявление не является исполняемым оператором, но присваивание явля¬ ется таковым. Мы используем оба метода инициализации переменных. Оператор ++counter; увеличивает счетчик цикла на 1 при каждом выполнении цикла. Условие про¬ должения цикла структуры while проверяет, является ли значение управляю¬ меньшим или 10 (это последнее значение, для равным которого условие истинно). Обратите внимание, что тело этого цикла while вы¬ полняется и в том случае, когда управляющая переменная равна 10. Цикл за¬ щей переменной вершается, когда управляющая переменная counter становится переменная становится (++counter <= 10 (т.е. равной 11). Программирующие на С сделали бы программу инициализировав переменную counter значением 0 и while больше на рис. 4.1 более краткой, while на заменив структуру 10) printf("%d\n",counter); Этот код экономит один оператор, непосредственно в условии структуры здесь устранены поскольку while перед фигурные скобки вокруг тела приращение выполняется проверкой. Кроме того, структуры while, поскольку его while теперь содержит только один оператор. Написание кода в такой сжатой манере требует некоторой практики.
141 Управление программой Распространенная ошибка программирования 4.1 Ввиду того, что значения с плавающей точкой могут быть неточными, управление циклами со счетчиком при помощи переменных с плавающей точкой может привести к неточным значениям счетчика и ошибочным результатам проверки на окончание цикла Хороший стиль программирования 4.1 Управляйте циклами Хороший счетчиком с помощью целочисленных значений стиль программирования 4.2 Делайте отступы Хороший со для операторов в теле каждой управляющей структуры стиль программирования 4.3 Помещайте пустую строку до и после каждой управляющей структуры большого раз¬ мера для выделения ее в тексте программы Хороший Слишком стиль программирования 4.4 большое число уровней вложенности может сделать для понимания. Пытайтесь избегать использования более трех Хороший программу уровней трудной вложенности. стиль программирования 4.5 Сочетание междустрочного интервала до и после управляющих структур и отступов в теле управляющих структур в ный вид, который пределах их заголовков значительно 4.4. повышает их придает программам двумер¬ удобочитаемость Структура повторения for Структура повторения for автоматически контролирует все детали повто¬ рения, управляемого счетчиком. Для иллюстрации всей мощи цикла for да¬ вайте перепишем программу на рис. 4.1. Результат показан на рис. 4.2. Программа работает следующим образом. Когда структура for начинает выполняться, управляющая переменная counter инициализируется значени¬ ем 1. Затем проверяется условие продолжения цикла counter ку начальное значение и оператор Затем <= 10. Посколь¬ переменной counter равно 1, условие удовлетворяется printf выводит значение переменной counter, а именно 1. в выражении counter++ происходит приращение управляющей пе¬ ременной counter и снова начинается выполнение цикла с проверки условия его продолжения. Поскольку ное значение не превышено, управляющая переменная теперь равна 2, конеч¬ и поэтому программа снова выполняет оператор printf. Этот процесс продолжается до тех пор, пока управляющая переменная это приводит к неу¬ counter не увеличивается до ее конечного значения 11 даче при проверке условия продолжения Выполнение программы продолжается for (в нашем случае с оператора return с цикла, и повторение завершается. первого оператора после структуры в конце программы).
Глава 4 142 /* счетчиком Повторение, управляемое */ <stdio.h> структуры #include mam с использованием for () { int /* counter 1; = инициализация, условие повторения /* все они включены в заголовок for (counter 1; counter <= 10; = приращение for */ */ counter++) counter); pnntf("%d\n", return и структуры 0; Рис. 4.2. Повторение, управляемое счетчиком с использованием структуры for На рис. 4.3 более подробно рассмотрена структура for, представленная на в ней рис. 4.2. Обратите внимание, что структура for «делает всю работу» необходимые для повторения, управляемого управляющей переменной. Если в теле цикла for содержится более одного оператора, для определения тела цикла требуются фигурные скобки. Обратите внимание, что на рис. 4.2 используется условие продолжение цикла counter <= 10. Если бы программист по ошибке написал counter < 10, то цикл был бы выполнен только 9 раз. Это является распространенной логи¬ специфицированы все элементы, счетчиком с использованием ческой ошибкой, называемой ошибкой смещения счетчика. Распространенная ошибка программирования 4.2 Использование неправильной операции отношения или неправильного конечного структур while или for может привести к ошибке смещения счетчика значения счетчика цикла в условии Хороший стиль программирования 4.6 Использование конечного значения в условии структур while или for и операции отно¬ шения <= помогает избежать ошибок сдвига счетчика Например, для цикла, использу¬ емого для вывода значений от 1 до 10, условием продолжения цикла должно быть co¬ unter <= 10, а не counter < 11 или counter < 10 Конечное значение управляющей переменной Имя управляющей переменной i for (int I counter т = 1; counter <= 10; т counter++) т Ключевое Начальное Приращение слово for значение управляющей переменной управляющей переменной Рис. 4.3. Компоненты типичного заголовка структуры for
143 Управление программой Общий формат структуры for for (выражение1; выражение2; выражениеЗ) оператор выражение1 инициализирует переменную управления циклом, выражение2 является условием продолжения цикла и выражениеЗ служит для прира¬ щения управляющей переменной. В большинстве случаев структура for может где быть представлена эквивалентной выражение! структурой while следующим образом: ; { (выражение2) оператор выражениеЗ; while } Из этого правила есть исключение, которое мы Часто выражение1 и выражениеЗ обсудим в разделе 4.9. выражений, разде¬ являются списками ленных запятыми. Запятые здесь на самом деле являются операциями-запятыми, которые гарантируют, что списки выражений будут оцениваться слева направо. Значением и типом списка выражений, разделенных запятыми, яв¬ Операция-запятая ляются значение и тип самого правого выражения в списке. в структуре for. Ее основным назначением является множественной инициализации и/или нескольких выражений чаще всего используется реализация приращения. Например, одной структуре for могут быть в две управляющих переменных, которые должны инициализироваться и получать приращение. Хороший стиль программирования 4.7 Помещайте в разделы инициализации и приращения структуры for только выраже¬ ния, включающие управляющие переменные. Манипуляции с другими переменными должны производиться либо перед циклом (если они выполняются только один раз, как операторы инициализации), либо в теле цикла (если они выполняются по одному разу за повторение, как операторы инкремента или декремента). Все три выражения в структуре for являются необязательными. Если опу¬ щено выражение2у С предполагает, что условие истинно, и возникает беско¬ нечный цикл. Выражение1 может быть опущено, если управляющая перемен¬ ная инициализируется в другом месте программы. ВыражениеЗ каться, если операторы приращения находятся в теле структуры может опус¬ for или если вообще не требуется. Выражение приращения в структуре for действует подобно автономному оператору С в конце тела цикла for. Поэтому приращение все нижеследующие counter = counter += выражения counter + 1 1 ++counter counter++ в блоке приращения структуры граммирующие на for являются эквивалентными. С предпочитают форму counter++, Многие про¬ так как приращение про¬ Поэтому постинкрементная форма Поскольку переменная, получающая здесь приращение в преинкрементной или постинкрементной форме, не входит в вы¬ ражение, обе формы приращения приводят к одному и тому же результату. Две исходит после выполнения тела цикла. представляется более естественной. точки с запятой в структуре for являются обязательными.
144 Глава 4 Распространенная ошибка программирования 4.3 Использование запятых вместо точек с запятой в заголовке структуры for. Распространенная ошибка программирования 4.4 Помещение точки с запятой сразу после заголовка структуры for делает тело этой структуры пустым оператором. Обычно это является логической ошибкой 4.5. Структура for: замечания и рекомендации 1. Блоки инициализации, условия продолжения цикла арифметические выражения. Например, и у 10, оператор содержать гут нии, что x for (j = = 2 в предположе¬ = <= j x; и приращения мо¬ * 4 x * у; j += у / x) эквивалентен оператору for 2. (j = 2; 80; <= j «Приращение» может j += 5) быть отрицательным (в вительности является декрементом, мом деле и этом случае оно в дейст¬ значение счетчика цикла на са¬ уменьшается). 3. Если условие продолжения цикла является ложным изначально, то тело цикла не выполняется. Вместо этого выполнение продолжается с оператора, следующего за структурой for. 4. Управляющая переменная часто выводится на печать или используется при вычислениях в теле цикла, но это не является обязательным. Ис¬ переменной пользование для управления повторением при отсутствии ссылок на нее в теле цикла 5. Блок-схемы структур for и довольно while распространенный случай. очень похожи. Например, на рис. 4.4 показана блок-схема оператора for (counter = printf("%d", counter <=10; 1; counter++) counter); Из этой блок-схемы становится понятно, что инициализация происхо¬ дит только один раз и что приращение происходит после выполнения оператора тела цикла. Обратите внимание, что (кроме кружков и стре¬ лок) блок-схема содержит только ромба. Снова вообразите себе, что символы прямоугольников и символ программист имеет доступ к большо¬ их столько, сколько может потребо¬ му ящику пустых структур for ваться программисту, чтобы путем их суперпозиции и вложения в дру¬ гие управляющие сформировать структурную реализацию Затем, опять же, прямоугольники и соответствующими алгоритму действиями и реше¬ структуры потока управления ромбы заполняются алгоритма. ниями. Хороший стиль программирования 4.8 Хотя значение управляющей переменной может модифицироваться в теле цикла for, это может привести к труднообнаружимым ошибкам. Лучше его не изменять.
145 Управление программой Задание counter начального = 1 значения управляющей переменной Проверка, не достигнуто ли конечное значение counter < < printf("%d", 10 = counter ++ counter); управляющей переменной Изменение Тело цикла (это может быть несколько операторов) управляющей переменной Рис. 4.4. Блок-схема типичной структуры for Примеры структур for 4.6. Следующие переменной в примеры демонстрируют способы изменения структуре for. a)Вариация управляющей переменной for i 1; = 100; <= i i + от 1 до 100 с управляющей приращением 1. +) b) Вариация управляющей переменной от 100 до 1 с приращением -1 (декрементом 1). for (i = i 100; >= i--) 1; c) Вариация управляющей переменной for (i = 7; <= i 77; i += for (i 20; 1 >= 2; i -= 7 до 77 с шагом, равным 7. от 20 до 2 с шагом, равным -2. 7) d) Вариация управляющей переменной = от 2) e) Вариация управляющей переменной следующей последовательности f) Вариация управляющей переменной по следующей значений: 99, 88, 77, 66, 55, 44, 33, 22, 11, 0. последовательности значений: for for (j (j = = по 2, 5, 8, 11, 14, 17, 20. 2; <= з 99; j >= 20; 0; j j +=3) -=11) В следующих двух примерах даны простые приложения структуры for. Программа на рис. 4.5 использует структуру for для суммирования 2 до 100. всех чет¬ ных целых чисел от но Обратите внимание, что тело структуры for на рис. 4.5 на самом деле мож¬ было бы соединить с правой частью заголовка структуры for, используя для этого операцию-запятую следующим образом:
146 Глава 4 for (number number 2; = 0 Инициализацию sum структуры for. = 100; <= += sum также можно number number, было бы объединить += с 2) блоком ини¬ циализации /* Суммирование с помощью #include <stdio.h> main () { int for (number = 2; number return <= 100; number += 2) += number; printf("Sum is %d\n", sum */ for number; 0, = sum структуры sum); 0; Sum is 2550 Рис. 4.5. Суммирование Хороший Хотя операторы, предшествующие структуре for, это делает Хороший структуры for стиль программирования 4.9 теле, часто могут льку с помощью и операторы, содержащиеся быть объединены в заголовке структуры for, избегайте этого, программу более трудной для чтения в ее поско¬ стиль программирования 4.10 Ограничивайте, если это возможно, размер заголовков управляющих структур одной строкой Во втором примере со структурой for вычисляются сложные проценты. Рассмотрим следующую постановку задачи: Некий клиент открыл в банке 5-процентный $1000.00. Предполагая, считайте и что вся прибыль выведите сумму денег сберегательный счет на депозите счета, рас¬ каждого года за 10 лет. остается на на счете на конец Для определения этих сумм используйте следующую формулу: а p( 1 + г)п где p первоначально вложенная сумма (т.е. сумма, на которую ются проценты) = r n годовая процентная начисля¬ ставка число лет сумма на депозите на конец п-го года. Эта задача требует цикла, в котором выполняется указанный расчет ба¬ ланса счета для каждого года 10-летнего периода. Решение задачи показано на рис. 4.6. а
147 Управление программой / * Вычисление сложных * / процентов <stdio.h> <math.h> #include #include main () { mt year; double amount, = principal pnntf("%4s%21s\n", "Year", for <= (year amount 1; = = year 10; * principal "Amount year++) + pow(1.0 printf("%4d%21.2f\n", rate 1000.0, year, = .05; deposit"); on { rate, year); amount); } return Year 0; Amount 1 2 3 4 5 6 deposit 1050.00 1102.50 1157.62 1215.51 1276.28 1340.10 7 1407.10 8 1477.46 9 1551.33 10 1628.89 on Рис. 4.6. Вычисление сложного процента с помощью for Структура for выполняет щую переменную от 1 до 10 10 раз тело цикла, изменяя при этом управляю¬ с приращением 1. Хотя в язык С не включена опе¬ рация возведения в степень, мы можем, тем не менее, вызвать для этой цели стандартную библиотечную функцию pow. Функция pow(x, у) вычисляет зна¬ чение x в степени у. Она принимает два параметра типа double и возвращает значение типа double. Тип double представляет собой тип с плавающей точ¬ кой, очень похожий на float, но в переменной типа double может храниться большее значение и с большей точностью, чем в переменной типа flo¬ at. Обратите внимание, что всякий раз, когда используется математическая функция типа pow, должен быть включен заголовочный файл math.h. На са¬ мом деле без включения файла math.h эта программа работала бы неправиль¬ гораздо но. что Функция включает ции pow типа требует двух параметров типа double. Обратите внимание, year является целым числом. Заголовочный файл math.h pow переменная информацию, которая преобразовать значение заставляет компилятор перед вызовом переменной year double. Эта информация содержится Прототипы функций обсуждаются в главе 5. Там в так называемом прототипе функ¬ ции pow. являются важным нововведением языка С же мы дадим краткое описание и других библиотечных Обратите математических функ¬ во временное представление ANSI функции pow и функций. внимание, что мы объявили переменные amount, principal и rate с типом double. Мы сделали это для простоты, поскольку имеем здесь дело с дробными долями доллара.
148 Глава 4 Хороший Не стиль программирования 4.11 используйте переменных типа занных с денежными суммами double или float для проведения вычислений, Неточность чисел с плавающей точкой свя¬ может вызвать к неправильным значениям денежных сумм. В упражнени¬ изучим возможности использования целых чисел для проведения вычислений, ошибки, которые приведут ях мы связанных с денежными суммами Вот простое пояснение того, что может происходить при использовании float или double для представления долларовых значений. Две денежные суммы, хранящиеся в машине в переменных типа float, мо¬ гут быть равны 14.234 (выводится в виде 14.23 со спецификацией преобразо¬ вания %.2f) и 18.673 (со спецификацией преобразования %.2f выводится в типов 18.67). При сложении этих значений получается сумма 32.907, которая спецификацией преобразования %.2f выводится в виде 32.91. Таким обра¬ виде со зом, ваша распечатка может выглядеть как 14.23 + 18.67 32.91 но очевидно, что сумма отдельных чисел, в том виде, как они напечатаны, дол¬ жна быть 32.90! переменной amount в программе используется спе¬ преобразования %21.2f. Число 21 в спецификаторе обозначает ши¬ рину поля, в которое будет выводиться значение. Ширина поля 21 указывает Для вывода значения цификатор на то, что выводимое значение во займет 21 позицию вывода. Число 2 специфи¬ (т.е. число позиций после десятичной точки). Если количест¬ отображаемых символов меньше ширины поля, то значение будет автома¬ точность цирует выровнено по правому краю. Это особенно значений с десятичной точкой, выводимых с одной тически выровнять ду % и значение по шириной левому краю, поля. Обратите полезно для выравнивания и той поместите символ внимание, же точностью. «-» Чтобы (знак минус) что знак минус также меж¬ может применяться для выравнивания по левому краю целых чисел в %-6d) и символьных строк эти мощные средства 4.7. (как, например, в %-8s). форматирования функций printf Структура (как, например, Мы подробно обсудим и scanf в главе 9. выбором со множественным switch обсуждали структуру с одним выбором if и структуру с двой¬ if/else. Иногда алгоритм содержит последовательность принятия решений, когда происходит независимая проверка переменной или выражения В главе 3 мы ным выбором на равенство каждому из константных целочисленных значений, которые они дейст¬ могут принимать, и в зависимости от этого предпринимаются различные вия. В языке С для обработки такого рода шений, предусмотрена структура Структура switch fault. В программе состоит из ряда меток case и на рис. личества различных ситуаций, связанных с принятием ре¬ со множественным выбором switch. необязательного блока de¬ 4.7 структура switch используется для подсчета буквенных ко¬ оценок, полученных студентами на экзамене.
149 Управление программой /* Подсчет буквенных #include <stdio.h> mam оценок */ () { int grade; int aCount dCount = 0, = 0, bCount fCount 0; = 0, cCount 0, = = printf("Enter the letter grades, \n"); pnntf("Enter the EOF character to end mput. while ( ( grade switch = getchar() { (grade) case 'A': case 'a': ++aCount; break; case 'В': case 'b' ++bCount; break; case 'С': case 'с' ++cCount; ) != EOF) /* switch, /* /* или { вложенная grade равна А а в grade равна /* или /* /* или /* /* или /* /* или /* этот в В в в while верхнем регистре в нижнем grade равна С с в нижнем /* b \n"); верхнем регистре в нижнем верхнем регистре */ */ */ */ */ */ */ break; case 'D': case 'd' ++dCount; break; case 'F': case ++fCount; break; case '\n': case 'f' grade равна D d в grade равна f в в нижнем F нижнем ввод верхнем регистре в верхнем регистре игнорируется */ */ */ */ */ break; default: /* ловит все другие символы */ pnntf("Incorrect letter grade entered."); printf(" Enter а new grade, \n"); break; printf("\nTotals for each letter grade are:\n") %d\n", aCount) printf("А %d\n", bCount) printf("В %d\n", cCount) pnntf ("С %d\n", dCount) printf("D %d\n", eCount) pnntf("E %d\n", fCount) pnntf("F return 0; Рис. 4.7. Пример использования структуры switch (часть 1 из 2)
150 Глава 4 Enter the letter grades. Enter the EOF character to end input. A B C c A D F C E Incorrect letter grade entered. Enter a grade. new D A B Totals A: 3 B: 2 C: 3 D: 2 F: 1 for each Рис. 4.7. В программе Пример ( grade are: использования структуры switch пользователь вводит заголовка структуры while letter буквенные (часть 2 из 2) оценки для группы. , Внутри while ( grade = getchar() ) != EOF) сначала выполняется заключенное в круглые getchar() ). Функция getchar (из стандартной скобки присваивание ( grade библиотеки ввода/вывода) счи¬ = тывает один символ с клавиатуры и сохраняет этот символ в ременной grade. Символы обычно хранятся важной особенностью языка С целочисленной в переменных типа пе¬ char. Однако является то, что символы могут храниться в лю¬ бом целочисленном типе данных, поскольку они представляются в компьютере в виде 1-байтовых целых чисел. Таким образом, мы можем обрабатывать сим¬ вол либо как целое число, либо как символ в зависимости от его смысла. На¬ пример, оператор printf("The character has the value %d.\n", (%c) использует спецификаторы преобразования % да символа а и его целочисленного значения. The character Целое число 97 (а) has the value с и %d 'a', 'a'); соответственно для выво¬ Результатом будет 97. является численным представлением символа в компьюте¬ Многие компьютеры в настоящее время используют набор символов ASCII (American Standard Code for Information Interchange), в котором число 97 ре. представляет символ строчной буквы a'. Список символов ASCII и их десяти¬ значений представлен в приложении Г. Символы можно считывать с по¬ чных мощью функции scanf со спецификатором преобразования %c. Операторы присваивания, взятые целиком, на самом деле обладают значе¬ нием. Это в точности слева от знака =. то самое переменной getchar() является символ, присваиваемый переменной grade. значение, Значением присваивания возвращаемый функцией getchar и которое присваивается grade =
151 Управление программой Тот факт, что операторы присваивания обладают значением, может оказа¬ ться полезным для инициализации нескольких переменных одним и тем же значением. Например, в операторе = а b = 0; = с сначала оценивается присваивание с справа = 0 (поскольку операция = ассоциируется 0 присваивается значение присваивания с налево). Затем переменной b = (которое равно 0). Затем переменной а присваивается значение присваивания b (с 0) (которое также равно 0). В программе значение присваивания grade = = = сравнивается со значением EOF getchar() «end of file» чает ет значение «конец -1) в (символом, мнемоника которого озна¬ принимаем EOF (который обычно име¬ контрольного значения. Пользователь вводит файла»). Мы качестве системно-зависимую комбинацию клавиш, означающую «конец меня нет файла», т.е. «У больше входных данных». EOF представляет собой символическую це¬ лочисленную константу, определенную в заголовочном файле <stdio.h> (мы 6). Если значение, увидим, как определяются символические константы, в главе присвоенное EOF равно EOF, программа завершается. Мы реши¬ переменной grade, ли представлять символы в этой программе (как имеет целочисленное значение Совет в виде значений типа мы уже говорили, int, поскольку обычно -1). по переносимости программ 4.1 Комбинации клавиш для ввода EOF Совет по переносимости (конца файла) являются системно-зависимыми. программ 4.2 Проверка значения на равенство символической константе EOF, а не числу -1 делает программы более переносимыми Стандарт ANSI требует, чтобы EOF являлась отри¬ цательным целым числом (но не обязательно -1) Таким образом, на различных вы¬ числительных На системах системах UNIX EOF может иметь и многих других различные значения индикатор EOF вводится нажатием <return> <ctrl-d> Эта запись означает нажатие клавиши возврата каретки менное нажатие корпорации DEC или и затем одновре¬ d. На других системах, например, VAX VMS MS-DOS корпорации Microsoft, индикатор EOF можно клавиш ctrl и ввести при помощи <ctrl-z> Пользователь вводит врата с клавиатуры оценки. (Enter) функция getchar считывает После одиночный нажатия клавиши воз¬ Если символ. EOF, происходит вход в структуру switch. За ключевым словом имя переменной grade в круглых скобках. Она является здесь он не равен switch следует управляющим выражением. Значение этого выражения сравнивается с каждой из меток case. Предположим, пользователь ввел в качестве оценки символ С. Символ С автоматически сравнивается имеет место соответствие с каждой меткой (case С :), case структуры случае символа С переменная cCount увеличивается на 1 происходит немедленный выход из структуры switch. Оператор break структуры него метки операторе switch. Если В выполняются операторы для этого case. и по оператору break вызывает передачу управления первому оператору после switch. Необходимость оператора break обусловлена тем, что без case оператора switch могли бы выполняться подряд. Если break в switch вообще отсутствует, то всякий раз, когда в структуре switch
152 Глава 4 имеет место соответствие, будут выполняться операторы для всех последую¬ особенность (Эта редко используется, хотя она идеально под¬ ходит для программирования итеративного стишка «Дом, который построил Джек».) Если не имеется ни одного соответствия, то выполняется блок default и выводится сообщение об ошибке. Каждый блок case может включать одно или более действий. Структура щих меток case. switch отличается от всех других структур тем, что в switch последовательность операторов требуется блоке case структуры в фигурные скобки. Общая блок-схема структуры со множественным выбором switch (с операторами break в каждом блоке case) приводится на рис. 4.8. случай не действия а в случае случай в случай break а действия ь break случае ь деиствия z в заключать случае break z действия по умолчанию Рис. 4.8. Структура со множественным switch выбором Из этой блок-схемы становится понятно, что каждый оператор break в конце блока case приводит к немедленной передаче управления за пределы структуры switch. Снова представьте себе, что программист имеет доступ к бо¬ льшому ящику пустых структур switch ваться программисту, чтобы путем их управляющие и структуры сформировать структурную управления алгоритма. Затем прямоугольники и ромбы ствующими алгоритму действиями потребо¬ их столько, сколько может суперпозиции и решениями. вложения в реализацию другие потока заполняются соответ¬
153 Управление программой Распространенная ошибка программирования 4.5 Пропуск оператора break структуре switch, когда он необходим в Хороший стиль программирования 4.12 Не забывайте о блоке default в операторах switch Случаи, явно не проверяемые в структуре switch, игнорируются Блок default помогает избежать этого, фокусируя внимание программиста на необходимости обрабатывать необычные условия Суще¬ ствуют ситуации, когда никакой обработки по умолчанию (в блоке default) не требу¬ ется Хороший стиль программирования 4.13 предложение default в структуре switch могут появляться в порядке, хорошим стилем программирования считается размещение предложения default в конце структуры Хотя предложения case и произвольном стиль Хороший программирования 4.14 структуре switch предложение default указано последним, опе¬ break для него не требуется Однако некоторые программисты вводят этот break для ясности и единообразия с метками case В том случае, когда в ратора В структуре switch case '\n': case на рис. ' 4.7 строки ': break; пробелы. Чтение проблемы. Чтобы заставить заставляют программу пропускать символы новой строки и символов по одному может вызывать некоторые программу считывать символы, их нужно отправить компьютеру путем нажа¬ тия на клавиатуре клавиши возврата. ток символа чтобы новой строки работала программа тываться Это приводит после символа, который по¬ правильно, этот символ новой в нашу структуру switch вышеприведенную сообщения об ошибке да во входном потоке встречаются символы Распространенная ошибка Отсутствие по обработки одному Хороший Не входной обработать. Часто, строки должен обраба¬ особым образом. Включив метку case, мы предотвращаем вывод лов к помещению во мы хотим о потоке Обратите в блоке default, или ког¬ пробелы. программирования 4.6 символов новой строки во входном потоке при чтении симво¬ может приводить к логическим ошибкам. стиль программирования 4.15 забывайте входном новой строки возможной при необходимости обработки считывании символов по символов новой строки во одному внимание, что перечисление вместе нескольких меток case (как, например, case 'D': case *d*: на рис. 4.7) просто означает, что для каждой из этих меток должна быть выполнена одна и та же последовательность действий. Применяя структуру switch, не забывайте, что она может быть использо¬ вана только для проверки константного целочисленного выражения, т.е. лю¬
Глава 4 154 бой комбинации символьных и целочисленных констант, которая раскрывает¬ ся в константное целое значение. Символьная константа представляется в виде отдельного символа в одиночных кавычках, как, например 'A'. Чтобы быть распознанными в качестве символьных констант, символы должны быть заключены в одиночные кавычки. лые значения. забывайте, В что Целочисленные константы это просто це¬ нашем примере мы использовали символьные константы. символы фактически представляют собой небольшие Не целые значения. Переносимые языки программирования, подобные С, должны иметь гибкие размеры типов данных. Различным приложениям могут понадобиться целые числа различных размеров. Язык С предусматривает несколько типов данных для представления целых чисел. Диапазон целочисленных значений для каж¬ дого типа зависит от аппаратных средств конкретного компьютера. В дополне¬ ние к типам int и char в С предусмотрены типы short (сокращение от short int) и long (сокращение от long int). Стандарт ANSI определяет, что минимальный диапазон значений для целых чисел типа short равен ±32767. Целых чисел типа long достаточно для подавляющего большинства целочисленных расчетов. Стандарт определяет, что минимальный диапазон значений для целых чисел типа long равен ±2147483647. На большинстве компьютеров тип int эквивален¬ тен типам short или long. Стандарт устанавливает, что диапазон значений для числа типа int по крайней мере совпадает с диапазоном значений целых чисел типа short и не превосходит диапазона значений целых чисел типа long. Тип данных char может применяться для представления целых ±127 или любого символа из набора чисел в диапазоне символов компьютера. Совет по переносимости программ 4.3 Поскольку long, тип int различается по размеру от системы к системе, пользуйтесь целыми целые числа вне диапазона ±32767 и хотели бы иметь возможность выполнять программу на различных компьютерных си¬ типа если вы рассчитываете обрабатывать стемах Совет по повышению эффективности 4.1 В случаях, ориентированных на память или льзование эффективность выполнения, когда нужно экономить необходимо быстродействие, может оказаться предпочтительным испо¬ целых 4.8. чисел меньших размеров Структура повторения do/while Структура повторения do/while подобна структуре while. В структуре whi¬ le условие продолжения цикла проверяется в начале цикла до выполнения его тела. Структура do/while проверяет условие продолжения цикла после выпол¬ нения тела цикла, поэтому тело цикла будет выполнено по крайней мере один раз. После завершения цикла do/while выполнение программы продолжается с оператора, следующего за предложением while. Обратите внимание, наличии только одного оператора в теле цикла в структуре ходимости использовать включаются Например, во do/while что при нет необ¬ фигурные скобки. Однако обычно фигурные скобки избежание путаницы между структурами do/while и while.
155 Управление программой while (условие) обычно рассматривается в заголовка структуры while. Структура вокруг ее тела, состоящего из одного оператора, качестве do/while без фигурных скобок выглядит как do оператор while (условие); что может приводить к путанице. Последняя строка быть неправильно while (условие); истолкована читателем как структура пустой оператор. Поэтому структура do/while может while, содержащая с одним оператором часто запи¬ сывается так: do { оператор (условие) while } Хороший ; стиль программирования 4.16 Некоторые программисты le, фигурные скобки в структуру do/while, необходимыми Это помогает отличить структуру do/whi¬ всегда включают даже если они не являются содержащую один оператор, от структуры while Распространенная ошибка программирования 4.7 Если условие продолжения цикла в структурах while, for или do/while становится ложным, возникают бесконечные циклы. Во избежание этого что после сразу заголовка структуры for или while нет точки с никогда не убедитесь, запятой. В цикле, управляемом счетчиком, убедитесь, что управляющая переменная в теле цикла уве¬ личивается (или уменьшается) В цикле, управляемом контрольным значением, убе¬ дитесь в том, что оно рано или поздно вводится В программе на рис. 4.9 структура сел от 1 до 10. Обратите do/while внимание, что к используется для вывода чи¬ управляющей переменной counter при проверке условия продолжения цикла применяется операция преинкре¬ мента. Обратите также внимание на фигурные скобки, заключающие тело структуры /* # do/while (состоящее Использование include из одного структуры оператора). повторения do/while */ <stdio.h> main () { int do counter 1; = { ", counter); printf("%d while (+ + counter <= 10) ; } return 0; } 1 2 3 4 5 6 7 8 9 10 Рис. 4.9. Использование структуры do/while
156 Глава 4 На рис. 4.10 представлена блок-схема структуры вится понятно, что условие действие будет продолжения цикла do/while. проверяется Из нее стано¬ только после крайней мере один раз. Снова обратите внимание, что (кроме кружков и стрелок) блок-схема содержит только символ прямоугольника и символ ромба. Снова представьте себе, что программист имеет доступ к большому ящику пустых структур do/while их столько, ско¬ лько может потребоваться программисту, чтобы путем их суперпозиции и вло¬ того, как выполнено по жения в другие управляющие структуры сформировать структурную реализа¬ цию потока управления алгоритма. Затем прямоугольники и ромбы заполня¬ ются соответствующими алгоритму действиями и решениями. действия true условие false Рис. 4.10. 4.9. Структура повторения do/while Операторы break и continue Операторы break и continue предназначены для изменения потока Исполнение оператора break в структурах while, for, do/while switch приводит к немедленному выходу из структуры. Выполнение про¬ управления. или граммы продолжается с первого оператора, следующего за ней. Обычным применением оператора break является досрочный выход из цикла или про¬ пуск оставшихся операторов в структуре switch (как на рис. 4.7). Рис. 4.11 демонстрирует оператор break if обнаруживает, в ak. Это завершает оператор for, ратора printf, структуре повторения for. Когда структура что значение x стало равным следующего за 5, выполняется оператор bre¬ а программа продолжает выполняться с опе¬ структурой for. Цикл выполняется полностью только четыре раза. Выполнение оператора continue в структурах while, for или приводит к пропуску оставшихся операторов в теле этой структуры do/while и выпол¬ цикла. В структурах while и do/while проверка цикла условия продолжения проводится сразу же после выполнения опера¬ тора continue. В структуре for выполняется выражение приращения, а затем проверяется условие продолжения цикла. Ранее мы установили, что в боль¬ нению следующей итерации шинстве случаев структура Одним из исключений for может является быть представлена структурой while. случай, когда выражение приращения в структуре while следует за оператором continue. В этом случае приращение
157 Управление программой проверкой условия продолжения цикла не выполняется, и структура работает не так, как структура for. На рис. 4.12 оператор continue служит в структуре for для пропуска оператора printf и начала следующей итерации цикла. перед while | /* Использование оператора break #include <stdio.h> I main() { int x; for ( if 1; = x (x 5 == 10; <= x x++) в структуре */ for { ) /* break; printf ("%d цикл прерывает только если x == 5 */ x) ; 11, } out printf("\nBroke return of loop == 5 at x == %d\n", x); 0; } 1 2 4 3 Broke out of loop at x Рис. 4.11. Использование оператора break в структуре for Использование /* оператора <stdio.h> #include mam continue в структуре for */ () { int x; for ( x if 1; = <= x ( x == 5 ) continue; pnntf("%d ", 10; /* x) x++) { пропускает оставшийся == x если 5 */ код в цикле только ; } pnntf("\nUsed continue return 1 2 Used 3 4 6 to skip printing the value 5\n"); 0; 7 8 continue 9 to 10 skip printing the value 5 Рис. 4.12. Использование оператора continue в структуре for
158 Глава 4 Хороший программирования 4.17 стиль Некоторым программистам кажется, что операторы break и continue нарушают нор¬ мы структурного программирования Поскольку результаты действия этих операторов могут быть достигнуты структурными средствами (о которых мы скоро узнаем), эти программисты не пользуются операторами break и continue эффективности 4.2 Совет по повышению Операторы break ются быстрее и continue, при условии их надлежащего использования, выполня¬ соответствующих структурных методов, о которых мы скоро узнаем Общее методическое замечание 4.1 Существует определенное противоречие между разработкой качественного програм¬ много обеспечения и разработкой программного обеспечения, работающего наибо¬ лее эффективно Часто одна из этих целей достигается за счет другой 4.10. Логические операции До сих пор мы изучали только простые условия, вроде tal > 1000 и number != sentinelValue. Мы выражали counter <= 10, to¬ эти условия с помощью операций отношений >, <, >= и <= и операций равенства и !=. При каж¬ дом принятии решения проверялось в точности одно условие. Если бы в про¬ цессе принятия решения нам понадобилось проверить много условий, мы дол¬ жны == были бы делать эти проверки if или if/else. в отдельных операторах или во вложенных структурах В языке С предусмотрены логические операции, можно формировать сложные условия путем гическими операциями являются посредством которых объединения более простых. Ло¬ && {логическое И), 11 (логическое ИЛИ) и ! (логическое НЕ, также называемое отрицанием). Мы на примерах рассмотрим каждую из этих операций. Предположим, что в некотором месте программы мы хотим обеспечить ис¬ тинность двух условий одновременно, прежде чем выберем определенную ветвь ее выполнения. В этом случае мы можем применить логическую опера¬ цию && следующим образом: if == (gender 1 && >= age 65) ++seniorFemales; Этот оператор if содержит два простых условия. Условие gender == 1 мо¬ жет оцениваться, например, для определения принадлежности определенного Условие age >= 65 оценивается для определения его принадлежности гражданам. Сначала оцениваются два простых и >= выше, чем приоритет условия, поскольку приоритет обеих операций операции &&. Затем в операторе if рассматривается объединенное условие лица к женскому полу. к пожилым == gender == 1 && age >= 65 Это условие является истинным тогда и только тогда, когда оба простых условия истинны. И наконец, если это объединенное условие действительно seniorFemales увеличивается на 1. Если одно или оба простых условия ложны, то программа игнорирует приращение и переходит к оператору, следующему за if. становится истинным, то счетчик
159 Управление программой Таблица на рис. 4.13 сводит воедино свойства операции &&. В таблице по¬ комбинации нулевых (false) и ненулевых (true) значений для выражений expressionl и expression2. Такие таблицы часто на¬ зывают таблицами истинности. В С все выражения, включающие операции от¬ ношений, операции равенства и/или логические операции, оцениваются как 0 казаны все четыре возможных 1. Хотя С устанавливает для выражений значение true, равное 1, любое ненулевое значение воспринимается как истинное. или expression1 expression2 expression1 && expression2 0 0 0 0 ненулевое 0 ненулевое 0 0 ненулевое ненулевое 1 Таблица Рис. 4.13. истинности операции && Теперь давайте рассмотрим операцию JJ (логического И) (логическое ИЛИ). Предполо¬ жим, что в некотором месте программы мы хотим обеспечить истинность хотя бы одного из двух условий, чтобы перейти к некоторой ее ветви. В этом случае мы применим операцию || в как , следующем фрагменте программы: if (semesterAverage printf("Student >= 90 grade fmalExam | | is >= 90) A\n"); Этот оператор также содержит два простых условия. 90 определяется, заслуживает mesterAverage >= свои занятия усердные xam >= чине протяжении 90 определяется, заслуживает выдающихся операторе результатов на семестра. Оценкой условия se- «А» за студент оценки Оценкой условия finalE- ли студент оценки заключительном «А» экзамене. за курс по при¬ После этого в if рассматривается объединенное условие semesterAverage и на ли >= 90 | | fmalExam >= 90 студент получает оценку «А», если одно или оба простых условия истинны. Обратите внимание, что сообщение «Student grade is А (Оценка студента: А)» не выводится только тогда, когда оба простых условия ложны (равны нулю). Рис. 4.14 показывает таблицу истинности для логической операции ИЛИ(Ц). expression1 expression2 expression1 0 0 0 0 ненулевое 0 ненулевое 0 0 ненулевое ненулевое 1 Рис. 4.14. Таблица истинности операции ^(логического expression2 ИЛИ)
160 Глава 4 Операция && рации И или имеет более высокий приоритет, чем операция слева направо. ассоциируются II . Обе опе¬ содержащее операции && Выражение, оценивается только до тех пор, пока не установлена его истинность , или ложность. Таким gender 1 == будет и образом, age >= оценка условия 65 если переменная прекращается, ложно), && gender не равна 1 (т.е. выражение продолжена, если переменная целом все еще может быть истинным, если gender равна age >= 65). 1 (т.е. в целом выражение в Совет по повышению эффективности 4.3 В выражениях, включающих операцию &&, делайте условие, ложность которого наи¬ более вероятна, крайним слева В выражениях, включающих операцию II делайте , крайним шить условие, истинность которого наиболее вероятна слева время выполнения Это может умень¬ программы В С предусмотрена операция ! (логическое отрицание), позволяющая про¬ смысл условия. В отличие от операций && и II кото¬ объединяют два условия (и, следовательно, являются бинарными опера¬ циями), операция отрицания имеет в качестве операнда только одно условие (и, следовательно, является унарной). Логическая операция отрицания поме¬ щается перед условием, когда мы хотим выбрать ветвь выполнения програм¬ граммисту «обратить» , рые мы с ложным дующем if (до отрицания) первоначальным условием, как, например, в сле¬ фрагменте программы: ( ! (grade sentmelValue) ) pnntf("The next grade is %f\n", == grade); Заключение условия grade sentinelValue в круглые скобки необходи¬ мо, поскольку логическая операция отрицания имеет более высокий приори¬ тет, чем операция равенства. Рис. 4.15 показывает таблицу истинности для == логической операции отрицания. expression !expression 0 1 ненулевое 0 Рис. 4.15. Таблица истинности операции ! В большинстве случаев программист (логического отрицания) может избежать использования логи¬ ческого отрицания, по-другому выражая условие с помощью операции отношения. Например, предыдущий соответствующей оператор может быть записан и так: if (grade != sentinelValue) next grade is pnntf("The В таблице операций на рис. языка 4.16 %f\n", grade); показаны приоритет и ассоциативность различных С, рассмотренных к настоящему моменту. Операции убывания их приоритета. ны сверху вниз в порядке показа¬
161 Управление программой Операции О ++ + -- ! - (тип) % / + V V == ! л II Л ii = Ассоциативность Тип слева направо круглые скобки справа налево унарные слева направо мультипликативные слева направо адитивные слева направо отношения слева направо равенства && слева направо логическое И II слева направо логическое ИЛИ ? ; += -= / *= % = = справа налево условные справа налево присваивания слева направо запятая Рис. 4.16. Приоритет и ассоциативность операций 4.11. Смешивание и Есть одна типичная операций равенства (==) присваивания (=) ошибка, которую программирующие С, на вне зависи¬ мости от их опыта, склонны делать настолько часто, что мы решили посвятить ей специальный раздел. Эта ошибка (равенства) на (присваивания) = тив, операторы с этими выполняются граммы или обычно мен связан с тем, что они случайной замене операции (реже) наоборот. Особый ущерб от этих состоит в не вызывают синтаксических нормального завершения, этом неправильные результаты из-за логических Можно выделить два аспекта Во-первых, любое выражение в возможно, и порождая ошибок времени про¬ при выполнения. С, с которыми связаны эти проблемы. С, вырабатывающее значение, может решения любой управляющей структу¬ языка языке быть использовано в блоке принятия ры. Если это значение равно 0, оно лично от нуля, то за¬ ошибок. Напро¬ ошибками обычно компилируются правильно до == обрабатывается обрабатывается как ложное, а если оно от¬ Второй аспект связан с тем, как истинное. С присваивание возвращает значение, а именно то значение, кото¬ присваивается переменной слева от знака =. Например, предположим, что что в языке рое мы намереваемся написать if (payCode 4) == pnntf("You get но случайно if а bonus!"); а bonus!"); мы пишем (payCode = 4) pnntf("You get оператор if, как и положено, выдает премию сотруднику, платеж¬ ный код которого равен 4. Второй оператор if тот, что содержит ошибку, Первый вычисляет значение выражения присваивания в условии выражение представляет собой простое присваивание, 6 Зак 801 структуры if. Это значением которого яв-
162 Глава 4 Поскольку любое ненулевое значение интерпретируется как «true», условие в этом операторе if всегда истинно и сотрудник всегда по¬ лучает премию, независимо от его фактического платежного кода! ляется константа 4. Распространенная ошибка программирования 4.8 Использование операции честве знака == для присваивания или использование операции = в ка¬ равенства 7 имя переменной сле¬ так, чтобы При обратном порядке записи константа была слева, а имя переменной справа, как в выражении 7 x, на операцию =, будет под¬ программист, случайно заменивший операцию страхован компилятором. Компилятор обработает это как синтаксическую ошибку, поскольку слева от операции присваивания может помещаться толь¬ ко имя переменной. По крайней мере, это предотвратит возможные разруши¬ Программисты обычно пишут в условиях типа x == ва и константу справа от знака =. == == тельные последствия логической ошибки времени выполнения. Говорят, слева»), что имена переменных являются lvalue (от «left value», «значе¬ поскольку они могут стоять слева от операции присваивания. Константы, напротив, являются rvalue (от «right value», «значение справа»), ние поскольку они могут находиться только в тите внимание, что не lvalue правой Обра¬ rvalue, но части присваивания. может использоваться также в качестве наоборот. Хороший Когда в стиль программирования 4.18 выражение равенства входят переменная и константа, как в случае x == 1, не¬ которые программисты предпочитают записывать в этом выражении константу слева, а имя переменной справа от знака ки, возникающей при случайной Есть и другая сторона медали, неприятной. Предположим, = в качестве замене защитной меры от логической программистом операции которая может оказаться == в ошиб¬ операцией равной = степени что программист хочет присвоить значение пере¬ менной посредством простого оператора вроде x = 1; но вместо этого пишет x == 1; Здесь это также не является синтаксической ошибкой. Компилятор про¬ сто оценивает выражение как условие. Если переменная x равна 1, условие ис¬ тинно и выражение возвращает значение 1. Если переменная x не равна 1, условие ложно и выражение возвращает значение 0. Вне зависимости от воз¬ вращаемого значения здесь отсутствует какая бы то ни было операция присва¬ ивания, поэтому это значение будет просто потеряно и значение x останется без изменений, вызывая, возможно, тем самым логическую ошибку времени выполнения. К сожалению, не существует удобного приема, чтобы помочь вам в разрешении этой проблемы!
163 Управление программой 4.12. сводка по структурному Краткая программированию Подобно тому, как архитекторы проектируют различные сооружения, ис¬ пользуя при этом коллективную мудрость представителей своей профессии, так же должны Наша профессия и поступать программисты беднее. Мы многому научились Возможно, наиболее важным из того, что тельно мощи при проектировании программ. моложе архитектуры и наша коллективная мудрость значи¬ структурного программирования проще для понимания (чем всего можно строить неструктурированные но, проще для тестирования, отладки, лишь за пять десятилетий. мы узнали, является то, что при по¬ программы, программы) модификации которые и, следователь¬ и даже для проверки их корректности с точки зрения математики. В главах 3 и 4 Каждая структура мой и обсуждалась представим мы сосредоточились на управляющих структурах языка в отдельности на примерах. С. была представлена вместе со своей блок-схе¬ Сейчас мы обобщим результаты глав 3 и 4 и правил для составления структурных программ и простой набор определения их свойств. На рис. 4.17 приводится суждавшимся в главах 3 и 4. общая сводка по управляющим структурам, об¬ Для указания единственной точки входа в каж¬ дую структуру и единственной точки выхода из нее на рисунке используются символы кружков. Соединение отдельных символов блок-схемы в произволь¬ ном порядке может привести к неструктурированным программам. Поэтому программисты решили, объединяя символы блок-схемы, сформировать огра¬ ниченный набор управляющих структур и создавать только структурные программы путем надлежащего объединения управляющих структур только двумя простыми способами. одним входом/одним Для выходом простоты используются только структуры с существует только один способ входа в каж¬ дую управляющую структуру и только один способ выхода из нее. Последо¬ вательное соединение управляющих структур для создания структурных точка выхода одной управляющей структуры непо¬ программ несложно средственно соединяется т.е. ры, грамме; с точкой входа следующей управляющей структу¬ управляющие структуры просто помещаются одна за мы назвали это «суперпозицией другой управляющих структур». в про¬ Кроме того, правила построения структурированных программ допускают вложе¬ ние управляющих структур. На рис. 4.18 показаны правила построения правильно структурированных Правила подразумевают, что символ прямоугольника может означать в блок-схеме любое действие, включая операции ввода/вывода. программ. Применение правил из рис. 4.18 всегда приводит к ме с отчетливыми компоновочными структурной блок-схе¬ блоками. Например, многократное приме¬ 2 к простейшей блок-схеме приводит к структурной блок-схесодержащей набор последовательных прямоугольников (рис. 4.20). Обра¬ нение правила ме, тите внимание, что по правилу другом управляющих структур; зиции. 6* 2 генерируется ряд расположенных друг за назовем правило 2 правилом суперпо¬ давайте
164 Глава 4 Рис. 4.17. Структуры языка С (последовательная, выбора и повторения) с одним входом/одним выходом
165 Управление программой Правила построения структурированных программ 1) Начинайте с «простейшей блок-схемы» 2) Любой прямоугольник (действие) (рис 419) может быть заменен двуя последовательными прямоугольниками (действиями) 3) Любой прямоугольник (действие) может быть заменен любой управляющей структурой (последовательной, if, if/else, switch, while, do/while или for 4) Правила 2 и 3 могут применяться неограниченное количество раз в произвольном порядке Рис. 4.18. Правила построения структурных программ Рис. 4.19. Простейшая блок-схема Правило 3 называется правилом вложения. Многократное применение правила 3 к простейшей блок-схеме приводит к блок-схеме с отчетливой вло¬ женностью управляющих структур. Например, на рис. 4.21 прямоугольник в простейшей блок-схеме сначала заменяется структурой с двойным выбором (if/else). Затем правило 3 снова применяется к обоим прямоугольникам в структуре с двойным выбором и происходит замена каждого прямоугольника своей структурой с двойным выбором. Изображенные штриховой линией пря¬ моугольники вокруг каждой из структур с двойным выбором представляют исходный прямоугольник. Рис. 4.20. Многократное применение правила 2 рис. 4.18 к простейшей блок-схеме
Глава 4 166 Рис. 4.21. Применение правила 3 рис. 4.18 к простейшей блок-схеме Правило 4 порождает большие по размеру, более сложные структуры с бо¬ глубоким вложением. Блок-схемы, которые возникают в результате при¬ менения правил на рис. 4.18, составляют в совокупности набор всех возмож¬ лее ных структурных блок-схем и, следовательно, набор всех возможных струк¬ турированных программ. То, что их компоновочные блоки никогда не перекрываются, является структурного подхода состо¬ ит в том, что мы ограничиваемся небольшим числом простых фрагментов с од¬ ним входом/одним выходом и компонуем их только одним из двух простых следствием исключения оператора goto. Красота способов. На рис. 4.22 показаны виды суперпозиции компоновочных блоков, возникающих в результате применения правила 2, и виды вложенных компо¬ новочных рисунке блоков, возникающих также в результате применения правила показаны перекрывающиеся компоновочные не могут появляться в структурированных нию оператора goto). 3. На этом блоки, которые блок-схемах (благодаря исключе¬
167 Управление программой Пакетированные стандартные блоки Вложенные стандартные блоки Частично перекрывающиеся стандартные блоки (недопустимые в структурированных программах) Рис. 4.22. Суперпозиция компоновочных блоков, вложенные компоновочные блоки и перекрывающиеся компоновочные блоки При условии соблюдения правил на рис. 4.18 неструктурированная блок-схема (как, например, на рис. 4.23) получена быть не может. Если у вас есть сомнения в том, является ли данная блок-схема структурированной, при¬ меняйте правила рис. 4.18 в обратном порядке стейшей блок-схеме. Если блок-схему ная блок-схема является и попытайтесь можно привести к структурированной; свести ее к про¬ простейшей, в противном случае она исход¬ таковой не является. Рис. 4.23. Структурное ваний Бома и Неструктурная блок-схема программирование поощряет простоту. В результате исследо¬ Якопини стало ясно, что программного управления: необходимы только три разновидности
Глава 4 168 Последовательное Выбор Повторение Последовательное управление тривиально. Выбор реализуется одним из трех способов: Структура if (один выбор) Структура if/else (двойной выбор) Структура switch (множественный выбор) На самом деле несложно показать, что простой структуры if достаточно все, что можно сделать с для обеспечения любой разновидности выбора помощью структур if/else и switch, может быть реализовано структурами if. Повторение реализуется одним из трех способов: Структура while Структура do/while Структура for Несложно показать, что структуры while достаточно для реализации лю¬ бой разновидности повторения. Все, что можно сделать с помощью структур do/while и for, может Объединение быть сделано посредством структуры while. любая разновидность про¬ этих результатов показывает, что граммного управления, которая когда-либо может понадобиться в программе на С, может быть выражена при помощи всего лишь трех видов управления: последовательного структуры if (выбора) структуры while И (повторения) объединяться только двумя способа¬ суперпозицией и вложением. Воистину структурное программирование поощряет простоту. В главах 3 и 4 мы обсуждали способы создания программ из управляющих структур, содержащих действия и решения. В главе 5 мы представим еще один элемент структурирования программ, называемый функцией. Мы нау¬ чимся создавать большие программы, комбинируя функции, которые в свою очередь состоят из управляющих структур. Мы обсудим также, каким обра¬ зом применение функций способствует многократному использованию про¬ эти управляющие структуры могут ми граммного кода. Резюме Циклом называется группа команд, повоторяемая компьютером много¬ кратно, пока будет удовлетворено некоторое условие завершения. повторения являются: повторение, управляемое счет¬ повторение, управляемое контрольным значением. не Двумя формами чиком, и Счетчик цикла служит для подсчета команд. Он увеличивается ся группа команд. Контрольные чаях, (обычно на количества повторений группы 1) всякий раз, когда выполняет¬ значения обычно применяются для управления в тех слу¬ когда точное число повторений заранее не известно, и цикл со-
169 Управление программой держит операторы, которые получают данные при каждом выполнении цикла. Контрольное даны значение вводится после того, как в программу все значимые элементы данных. выбираться с Контрольные были пере¬ значения должны так, чтобы их нельзя было спутать с осторожностью, допустимыми элементами данных. повторения for автоматически контролирует все детали по¬ вторения, управляемого счетчиком. Общий формат структуры for Структура for (выражение1; выражение2; выражениеЗ) оператор выражение1 инициализирует переменную управления циклом, выражение2 является условием продолжения цикла и выражениеЗ слу¬ жит для приращения управляющей переменной. где Структура однако в условие продолжения цикла проверяется в конце повторения do/while структуре do/while подобна цикла, поэтому тело цикла Формат для оператора будет do/while структуре выполнено по while, крайней мере один раз. do оператор while (условие) ; Исполнение оператора break в какой-либо из структур повторения while (for, приводит к немедленному выходу из структуры. Вы¬ полнение продолжается с первого оператора после цикла. и do/while) Исполнение оператора continue (for, while в какой-либо из структур повторения приводит к пропуску всех оставшихся операто¬ в и теле ров структуры переходу к следующей итерации цикла. do/while) и Оператор switch обрабатывает последовательность решений, когда про¬ исходит проверка конкретной переменной или выражения на равенство каждому из значений, которые они могут принимать, и предпринима¬ действия. Каждая метка case в операторе switch может ются различные вызывать грамм выполнение нескольких после операторов каждого операторов. блока case В большинстве про¬ необходим оператор break, случае программа будет выполнять операторы всех блоков case до тех пор, пока не будет встречен оператор break или достигнут конец оператора switch. В нескольких блоках case могут выполняться в противном одни и те же операторы, если метки этих case будут перечислены вмес¬ те перед этими операторами. В структуре switch могут проверяться то¬ лько константные целочисленные выражения. Функция getchar возвращает с клавиатуры (стандартный ввод) один символ в виде целого числа. На системах UNIX и многих других символ EOF вводится последовате¬ льностью <return> На <ctrl-d> системах VMS и DOS символ EOF вводится посредством <ctrl-z> Логические операции можно использовать для условий. Логическими операциями являются формирования сложных и !, означающие &&,
170 Глава 4 соответственно логическое И, логическое ИЛИ и логическое НЕТ (отри цание). Истинным Ложным является значением значением является 0 любое ненулевое значение. (нуль). Терминология break множественный выбор char набор ASCII continue <ctrl-z> начальное значение управляющей переменной неопределенное повторение определенное повторение ошибка смещения счетчика double long lvalue («значение слева») <return><ctrl-d> rvalue («значение справа») short бесконечный цикл блок default в структуре switch вложенные управляющие структуры выравнивание по левому выравнивание по правому краю краю декремент знак по для минуса левому конец выравнивания краю файла конечное значение переменной контрольное управляющей циклом переменная управления повторение, управляемое правило вложения правило суперпозиции счетчиком управляющей переменной простое условие структура выбора switch структура повторения do/while структура повторения for структура повторения while структуры повторения приращение счетчик цикла таблица истинности тело цикла (одноместная) операция управляющие структуры с одним унарная значение логические операции логическое И логическое ИЛИ логическое отрицание метка символов входом / одним выходом (&&) условие продолжения цикла (||) функция (!) case getchar функция pow ширина поля Распространенные ошибки программирования что значения с плавающей точкой могут быть неточны¬ ми, управление циклами со счетчиком при помощи переменных с плавающей точкой может привести к неточным значениям счетчика и ошибочным проверкам на окончание цикла. 4.1. Ввиду того, 4.2. Использование 4.3. Использование запятых вместо точек с запятой в заголовке структу¬ ры for. 4.4. Помещение неправильной операции отношения или неправиль¬ ного конечного значения счетчика цикла в условии структур while или for может привести к ошибке смещения счетчика. точки с запятой сразу после заголовка структуры for де¬ этой структуры for пустым оператором. Обычно это явля¬ логической ошибкой. лает тело ется 4.5. Пропуск оператора break в структуре switch, когда он необходим.
171 Управление программой 4.6. Отсутствие обработки чтении символов по символов одному новой строки может во входном потоке при приводить к ошиб¬ логическим кам. 4.7. Если условие do/while циклы. продолжения цикла в Во избежание for структуры или этого while счетчиком, убедитесь, убедитесь, нет точки с что while, for структурах никогда не становится ложным, или возникают бесконечные что сразу после заголовка запятой. В цикле, управляемом управляющая в переменная теле цикла (или уменьшается). В цикле, управляемом контроль¬ значением, убедитесь в том, что оно рано или поздно вводится. увеличивается ным 4.8. Использование операции = операции Хороший стиль в качестве == для присваивания или использование знака равенства. программирования 4.1. Управляйте циклами чений. 4.2. Делайте отступы для операторов со счетчиком с помощью целочисленных в теле зна¬ каждой управляющей струк¬ туры. 4.3. Помещайте пустую строку до и после каждой управляющей структу¬ ры большого размера для выделения ее в тексте программы. 4.4. Слишком большое число 4.5. уровней вложенности может сделать про¬ грамму понимания. Пытайтесь избегать использования более вложенности. трудной для трех уровней Сочетание междустрочного интервала до структур и отступов в теле двумерный удобочитаемость. головков придает программам повышает их 4.6. Использование и после управляющих структур вид, управляющих в пределах их за¬ который конечного значения в условии структур значительно while или for операции отношения <= помогает избежать ошибок сдвига счетчика. Например, для цикла, используемого для вывода значений и от 1 до а 4.7. не должно быть counter <= 10, в разделы инициализации и приращения структуры for 10, условием продолжения цикла counter Помещайте 11 < только выражения, пуляции или counter < 10. включающие управляющие переменные. с другими переменными должны производиться циклом (если они выполняются инициализации), либо только в теле цикла (если один раз, как теле кам; 4.9. значение цикла for, либо перед операторы они выполняются по одно¬ му разу за повторение, как операторы инкремента или 4.8. Хотя Мани¬ декремента). управляющей переменной может быть изменено в это может привести к труднообнаружимым ошиб¬ лучше его не изменять. for, и операторы, со¬ в заголовке в ее часто быть теле, объединены могут держащиеся структуры for, избегайте этого, поскольку это делает программу бо¬ лее трудной для чтения. Хотя операторы, предшествующие структуре
172 Глава 4 4.10.0граничивайте, щих если это возможно, 4.11.He используйте переменных вычислений, размер управляю¬ типа double или float для проведения Неточность чисел с связанных с денежными суммами. плавающей точкой может вызвать ошибки, которые приведут В упражнениях правильным значениям денежных сумм. возможности использования ний, заголовков одной строкой. структур связанных 4.12.He забывайте с целых чисел денежными к не¬ мы изучим для проведения вычисле¬ суммами. блоке default в операторах switch. Случаи, явно не switch, игнорируется. Блок default помога¬ ет избежать этого, фокусируя внимание программиста на необходи¬ мости обрабатывать необычные условия. Существуют ситуации, ког¬ да никакой обработки по умолчанию (в блоке default) не требуется. о проверяемые в структуре 4.13.Хотя предложения case и предложение default в структуре switch могут появляться в произвольном порядке, хорошим стилем про¬ граммирования считается размещение предложения default в конце структуры. 4.14. В том случае, когда в структуре switch предложение default указано последним, оператора break для него не требуется. Однако некото¬ рые программисты вводят этот метками break для ясности и единообразия с case. 4.15.He забывайте вой строки во о возможной необходимости обработки символов но¬ входном потоке при считывании символов по одному. 4.16.Некоторые программисты всегда включают фигурные скобки в структуру do/while, даже если они не являются необходимыми. Это помогает отличить структуру do/while, содержащую один оператор, от структуры while. 4.17.Некоторым nue программистам кажется, что операторы break нарушают нормы структурного программирования. и conti¬ Поскольку результаты действия этих операторов могут быть достигнуты струк¬ турными средствами (о которых мы скоро узнаем), эти программи¬ сты не 4.18. Когда пользуются операторами break и continue. в выражение равенства входят переменная и константа, как в 1, некоторые программисты предпочитают записывать в переменной справа от знака в качестве защитной меры от логической ошибки, возникающей при случайной замене программистом операции операцией =. случае x == этом выражении константу слева, а имя = == Советы по повышению 4.1. эффективности В случаях, ориентированных нужно оказаться предпочтительным эффективность выполнения, когда необходимо быстродействие, может на экономить память или использование целых чисел меньших размеров. 4.2. Операторы break вания, и continue, при условии их надлежащего использо¬ быстрее соответствующих структурных мето¬ выполняются дов, о которых мы скоро узнаем.
173 Управление программой 4.3. В выражениях, включающих операцию ность которого включающих операцию ность которого полнения &&, делайте условие, наиболее вероятна, крайним I слева. В выражениях, I, делайте условие крайним слева, наиболее вероятна. Это лож¬ истин¬ может уменьшить время вы¬ программы. Советы по переносимости программ 4.1. Комбинации клавиш для ввода EOF (конца файла) являются систем¬ но-зависимыми. 4.2. Проверка значения на равенство символической константе EOF, а не -1 делает программы более переносимыми. Стандарт ANSI требует, чтобы EOF являлась отрицательным целым числом (но не числу обязательно -1). Таким образом, на различных вычислительных темах EOF может иметь различные значения. 4.3. Поскольку тип int различается целыми типа льзуйтесь long, сис¬ по размеру от системы к системе, по¬ если вы рассчитываете обрабатывать целые числа вне диапазона ±32767 и хотели бы иметь возможность выполнять программу на нескольких различных компьютерных сис¬ темах. Общие 4.1. методические замечания разработкой качест¬ разработкой программного Существует определенное противоречие между венного программного обеспечения и обеспечения, работающего наиболее эффективно. Часто одна целей достигается за счет Упражнения 4.1. из этих другой. для самоконтроля Заполните пробелы в каждом из a) Повторение, управляемое следующих счетчиком, утверждений. также называется повторением, поскольку заранее известно, сколько раз будет выполнен b) Повторение, ется лько раз будет цикл. управляемое контрольным значением, также называ¬ повторением, поскольку заранее не известно, ско¬ выполнен цикл. c) В повторении, управляемом счетчиком, рений группы команд используется для подсчета числа повто¬ . d) Оператор при его исполнении в структуре повторения вызывает немедленное выполнение следующей итерации цикла. при его исполнении в структуре повторения или структуре switch вызывает немедленный выход из структуры. e) Оператор f) выражения используется для проверки данной на равенство значений, которые 4.2. Установите, неверными. они являются каждому могут ли из постоянных переменной или целочисленных принимать. следующие высказывания верными Если высказывание неверно, объясните почему. или
Глава 4 174 a) В структуре выбора switch необходим блок default. b) В блоке default структуры выбора switch необходим оператор && > break. c) Выражение (x b) истинно, если истинно либо выраже¬ у ние x > у, либо выражение а < b. d) Выражение, а < содержащее операцию I I если истинно, , истинен один из или оба его операнда. 4.3. Напишите оператор С или последовательность операторов для реше¬ ния каждой из следующих a) Просуммируйте этого структуру sum ременные b) Выведите for. Предположите, и что 1 до 99, используя для были объявлены целые пе¬ count. 333.546372 значение 1, 2, 3, 4 шириной с поля 15 символов 5. Выровняйте выводимые значения Какими будут эти пять выводимых значений? точностью краю. задач: нечетные целые числа от c) Вычислите и значение 2.5 в степени Выведите результат цию pow. с с по левому 3, используя для этого функ¬ 2 и с шириной поля 10 точностью позиций. Каким будет выводимое значение? d) Выведите целые и числа от переменную-счетчик объявлена, x. 1 до 20, используя для Предположите, но не инициализирована. что Выводите Подсказка: используйте вычисление чение равно 0, выводите символ новой строки, выводите символ табуляции. лых чисел. e) Повторите 4.4. упражнение 4.3 Найдите ошибку (d) этого цикл while x была переменная в строке только x % 5. Если в противном случае с использованием структуры в каждом из следующих фрагментов x 1; while (x = <= 10); x++; } .1; у != 1.0; printf("%f\n", у); b) for c) switch у += .1) printf("The case 2: number is l/n"); pnntf("The number is 2/n"); number is not = (у (n) case { 1: break; default: printf("The 1 or 2\n"); break; } d) Следующий n = код должен выводить значения от 1 до 10. 1 ; while (n < 10) printf("%d ", n++); for. кода и объяс¬ ните, как ее можно исправить. a) 5 це¬ это зна¬
175 Управление программой Ответы 4.1. к упражнениям для самоконтроля а) определенным, b) неопределенным, с) контрольная переменная или d) continue. e) break. f) счетчик, 4.2. а) Неверно. Блок default ется действия никакого не является по структура выбора switch. обязательным. Если то умолчанию, нет не требу¬ необходимости и в блоке default. b) Неверно. Оператор break используется для выхода из структуры switch. Оператор break не является необходимым, если блок default является блоком структуры. последним c) Неверно. При использовании операции && для того, чтобы истин¬ ным было все выражение, должны быть истинными оба выражения отношений. d) Верно. 4.3. а) = sum for 0; (count sum += 1; = count <= 99; 2) 333.546372); 333.546372); 333.546372); /* /* /* выводит 333.5 */ выводит 333.55 */ выводит 333.546 printf( "%-15.4f\n", printf( "%-15.5f\n", 333.546372); 333.546372); /* /* выводит 333.5464 выводит 333.54637 V */ */ = x 1; while (x { x) ; if (x % 5 0) printf ("\n"); else <= 20) printf("%d", == printf("\t"); x++; или x = 1 ; while (x <= if (x % 20) 5 == 0) printf("%d\n", else x++)j printf("%d\t", x++)i или x = 0 ; while if e) += pnntf ( "%-15.lf\n", printf( "%-15.2f\n", printf( "%-15.3f\n", c) printf("%10.2f\n", pow(2.5, d) count count; for (++x (x % <= 5 20) == 0) printf("%d\n", else x); printf("%d\t", x); (x = 1; x <= printf("%d", if (x % 5 == 20; x); 0) printf("%d\n"); x++) 3) /* выводит 15.63 */
176 Глава 4 else printf("\t"); } или for 4.4. 1; x % 5 (x if (x = x+ + ) 20; <= == 0) printf("%d\n", else x); printf("%d\t", x); а) Ошибка: помещение точки с запятой после заголовка структуры while приводит к бесконечному циклу. Исправление: b) Ошибка: ния замените точку с запятой структурой 1; = у != }. для управле¬ требуемые значения. 10; pnntf("%f\n", c) Ошибка: или удалите ; и целое число и выполните соответствую¬ щее вычисление, чтобы получить (у { повторения for. Исправление: используйте for символом плавающей точкой использование числа с у++) (float) у / 10); отсутствие оператора break в первом блоке case. Исправление: добавьте оператор break в конец первого блока case. Обратите внимание, что это не обязательно является ошибкой, если программист хочет, кий раз, когда чтобы оператор блока case 2: выполнялся оператор блока case 1:. d) Ошибка: используется неправильная операция вии вся¬ выполняется продолжения повторения структуры Исправление: используйте операцию <= отношения в усло¬ while. вместо <. Упражнения 4.5. Найдите ошибку в каждом из следующих фрагментов кода (Приме¬ может быть более одной ошибки): чание: a) for (x 100, x >= 1, printf("%d\n", x); = x++) b) Следующий код должен выводить, является ли данное целое чис¬ ло нечетным или нечетным: switch case (value 0: % 2) { pnntf("Even case integer\n"); 1: pnntf("Odd mteger\n"); } c) Следующий код должен вводить целое их. Предположите, что пользователь scanf("%d", &mtVal); charval getchar(); printf("Integer: %d\nCharacter: число и символ и выводить вводит с клавиатуры 100 А. = d) for (x = .000001; x pnntf("%.7f\n", <= x); .0001; x %c\n", += intVal, .000001) charVal);
Управление программой 177 e) Следующий код должен выводить нечетные целые числа от 999 до 1: for (x f) Следующий Do } { if код должен выводить четные целые числа от 2 до 100: (counter % 2 printf("%d\n", == g) Следующий код (предположите, что total 4.6. Установите, каждым 100; = (x из а) for(x += for(x for (x for (x = = for (x = 13; <= x <= x 3; 1; управляющей переменной 0): x x выводятся x += 2) += 7) x) ; , 15; , x) 5; , >= , x += 3) r 7) += x x) 2; x) x i и U) ; Напишите операторы for, которые выводят следующие последовате¬ льности значений: a) 1, 2, 3, 4, 5, 6, 7 b) 3, 8, 13, 18, 23 c) 20, 14, 8, 2, -4, -10 d) 19, 27, 35, 43, 51 4.8. значением операторов for: 22; x x) ; <= x 12; , <= x printf("%d\n" 4.7. total инициализирована x++); x; printf ("%d\n" e) 150; <= printf("%d\n" d) ; переменная x printf("%d\n" с) 100) какие значения 5; = 0) counter); должен суммировать целые числа от 100 до 150 printf("%d\n" b) < следующих 2; = 2) 2; = counter += 2; While (counter for += x 1; x); ("%d\n", printf counter >= x 9 99; = Что делает следующая программа? #include <stdio.h> main () { int i, j, x, у; pnntf("Enter integers in the range 1-20: scanf("%d%d", &x, &y); for (i 1; i <= у; i++) { = for (j = 1; j <= x; printf("0"); printf ("\n"); } return 0; j++) ")
178 Глава 4 4.9. Напишите Предположите, что scanf, определяет помощью ввести. которая суммирует последовательность це¬ программу, лых чисел. целое первое количество число, считываемое значений, которое с осталось Ваша программа должна считывать только одно значение scanf. Типичной входной последовательно¬ при каждом выполнении стью могло 5 бы быть 200 100 300 где 5 указывает, ний. 400 500 что должны суммироваться последующие 4.10.Напишите программу, которая ние нескольких для значением, считываемым с Предположите, что последнем scanf, является контрольное помощью 9999. Типичной входной последовательностью значение значе¬ вычисляет и выводит среднее значе¬ чисел. целых 5 могла бы быть последовательность 10 8 11 7 9 указывающая, 9999 что должно быть вычислено среднее для всех значе¬ ний, предшествующих 9999. 4.11.Напишите программу, которая целых чисел. ляет Предположите, количество оставшихся находит наименьшее из нескольких что первое считанное значение опреде¬ значений. 4.12.Напишите программу, которая целых чисел от 2 до 30. вычисляет и выводит сумму четных 4.13.Напишите программу, которая вычисляет нечетных целых чисел от 1 до 15. 4.14.Функция факториала ятности. n! Факториал и произносится ет факториалы лений выводит произведение часто используется в задачах по теории веро¬ положительного целого числа n «п ных целых чисел от и факториал») 1 до n. (записывается равен произведению положитель¬ Напишите программу, которая целых чисел от 1 до 5. вычисля¬ Выведите результаты табличной форме. Какие сложности вычислить факториал числа 20? в могли бы вычис¬ помешать вам 4.15.Измените программу вычисления сложного процента из раздела 4.6 так, чтобы она повторяла свои шаги для процентных ставок 5 про¬ центов, 6 процентов, 7 процентов, 8 процентов, 9 процентов процентов. Для варьирования процентной ставки используйте и 10 цикл for. 4.16.Напишите программу, которая другим следующие рисунки. по Для отдельности выводит один под генерации рисунков используйте циклы for. Все звездочки (*) должны выводиться единственным опе¬ printf в виде printf("*"); (это вызывает вывод звездочек ря¬ друг с другом). Подсказка: для последних двух рисунков необ¬ ратором дом ходимо, чтобы каждая строка начиналась с соответствующего числа пробелов.
179 Управление программой (А) (В) (С) (D) * * ** ********* *** ******** **** ******* ***** * * * ** * ****** ***** ******* **** ******** *** ********* ** ********** * 4.17. В период ** ********* ******** *** ** ***** **** ****** ***** ***** **** ******* *** ******** ** ********* * ********** востребование экономического спада денежного долга зна¬ чительно затрудняется, поэтому компании могут ограничивать пре¬ дельные размеры кредитов своих клиентов, чтобы не допустить раз¬ растания их годных к должны (денег, которые клиенты длительный спад некая компания принятию счетов компании). В ответ на урезала предельные размеры кредитов своих клиентов наполовину. Таким образом, если данный клиент имел предельный размер кре¬ дита в $2000, теперь предельный размер кредита этого клиента ра¬ вен $1000. Если клиент имел предельный размер кредита в $5000, теперь предельный размер кредита пишите программу, клиентов этой 1. счета Номер которая компании. этого клиента равен состояние анализирует Для $2500. На¬ кредита трех каждого клиента вам известны: клиента 2. Предельный размер кредита до спада 3. Текущий баланс (т.е. клиента сумма, которую клиент должен компании). Ваша программа должна вычислить и вывести на печать новое зна¬ предельного размера кредита для каждого клиента и опреде¬ чение лить (и распечатать) список тех клиентов, текущие балансы кото¬ рых превышают их новое значение предельного размера кредита. 4.18.0дним интересным приложением компьютеров является рисование графиков и столбцовых диаграмм (иногда называемых «гистограм¬ мами»). Напишите программу, которая считывает пять чисел (каж¬ дое между 1 и 30). Для каждого считанного числа ваша программа должна вывести строку из равного этому числу количества смежных звездочек. она Например, должна вывести если 4.19.Фирма, занимающаяся изделия число семь, заказами по почте, продает пять различных изделий, розничные таблице: видов Номер ваша программа считывает *******. цены на которые показаны в Розничная цена 1 $2 98 2 $4 50 3 $9 98 4 $4 49 5 $6 87 следующей
180 Глава 4 считывает последовательность пар Напишите программу, которая чисел следующим образом: 1. Номер изделия 2. Количество, проданное за один день Ваша программа должна использовать оператор switch, чтобы опре¬ делить розничную цену за каждое изделие. Программа должна вы¬ числить и отобразить на экране итоговую сумму по розничной про¬ даже всех изделий за последнюю неделю. 4.20. Закончите следующие таблицы истинности, заполняя каждый про¬ бел 0 или 1. Условие1 Условие2 Условие1 && Условие2 0 0 0 0 ненулевое 0 ненулевое 0 ненулевое ненулевое Условие1 Условие2 Условие1 0 0 0 0 ненулевое 1 ненулевое 0 ненулевое ненулевое Условие1 !Условие1 0 1 || Условие2 ненулевое программу на рис. 4.2 так, чтобы инициализация пере¬ менной counter выполнялась при объявлении, а не в структуре for. 4.21.Перепишите 4.22. Измените программу на рис. 4.7 так, чтобы она вычисляла среднюю оценку для группы. 4.23.Измените программу на рис. 4.6 так, чтобы для вычисления слож¬ ных она процентов обрабатывайте использовала только центов. Затем «разбейте» результат для этого вьте десятичную " %d" i 2, k 1, j операторов? = = == 1) ; b) printf("%d", j == 3) ; c) printf("%d", i >= 1 && d) printf("%d", m <= 99 e) printf("%d", j >= ( (Подсказка: на доллары и центы, , значений используя модуля. Вста¬ точку.) что следующих a) pnntf числа. соответственно операции деления и взятия 4.24.Предположим, из целые все денежные суммы в виде целочисленных i i k && || < j k = 3 4); < m); == m); и m = 2. Что выводит каждый
181 Управление программой + f) printf("%d", к g) pnntf("%d", !m) h) printf("%d", !(j i) j) < m j - >= к) ; ; - m)) printf("%d", !(к > m)) printf ( "%d" ! (з > к) ) , 3 | | j . 4.25. Выведите таблицу, состоящую из десятичного, ричного и шестнадцатеричного эквивалентов. этими системами упражнение, 4.26.Вычислите 4 71 = значение 4 + 4_ з счисления, 4 4 + ~ 7 5 но хотите прочитайте сначала n с Сколько это бесконечного ряда 4 Ц+К показывает ванное одним членом этого ряда, впервые выполнить Д. ~ 9 Выведите таблицу, которая т.д. восьме¬ вы не знакомы с попытаться приложение помощью двоичного, Если значение тг, двумя членами, аппроксимиро¬ тремя членами и членов этого ряда нужно использовать, прежде чем вы получите 3.14? 3.141? 3.1415? 3.14159? треугольник может иметь стороны, каждая из которых является целым числом. Набор трех целочисленных значений для сторон прямоугольного треугольника называется пифагоровой тройкой. Эти три стороны должны удовлет¬ 4.27.(Пифагоровы тройки.) Прямоугольный сумма квадратов двух катетов равна квадрату гипотенузы. Найдите все пифагоровы тройки для ка¬ тетов sidel, side2 и гипотенузы, не превосходящих 500. Используйте ворять следующему соотношению: цикл for с все тройной возможности. грубой ния. силы». Но вложенностью, в котором просто Это является Многим оно не примером перебираются вычисления «с помощью приносит эстетического удовлетворе¬ есть много причин, по которым эти методы важны. Во-пер- возрастающей такими вых, при мощности вычислительной техники, необыкновенными темпами, решения, для получения которых пона¬ добились бы годы или даже столетия компьютерного времени при использовании технологий, применявшихся всего лишь несколько лет тому назад, теперь могут быть получены за часы, минуты или Современные микропроцессорные схемы могут обра¬ батывать более 100 миллионов операций в секунду! И в 90-е годы, по даже секунды. всей вероятности, должны появиться микропроцессорные схемы, способные обрабатывать миллиард операций в секунду. Во-вторых, как вы узнаете из курсов по информатике для продолжающих обу¬ чение, существует большое число интересных задач, для которых не известны алгоритмические подходы, отличные от решения «с помо¬ грубой силы». В этой книге мы исследуем многие разновидно¬ методологий решения задач. Мы рассмотрим многие подходы к различным интересным проблемам, связанные с решением «с помо¬ щью грубой силы». щью сти 4.28. Компания как администраторам (это те, фиксированную зарплату), работникам с почасовой оплатой (те, кто получает фиксированную почасовую оплату за первые 40 отработанных часов и «полуторную», т.е. пре¬ платит своим служащим кто получает еженедельно
182 Глава 4 вышающую в 1.5 раз их почасовую, оплату за сверхурочные часы работы), работникам с комиссионным вознаграждением (те, кто по¬ лучает $250 плюс 5.7% от их валовых еженедельных продаж) и ра¬ ботникам со сдельной оплатой труда (те, кто получает фиксирован¬ ную сумму денег за каждую единицу произведенной ими каждый сдельщик в этой компании работает только с одной разновидностью продукции). Напишите программу для расче¬ та еженедельной зарплаты каждого служащего. Вам неизвестно за¬ продукции ранее количество служащих. Служащий каждой из групп имеет соб¬ ственный код оплаты: администраторы имеют код оплаты 1, работники с почасовой оплатой имеют код 2, работники с комисси¬ онным вознаграждением имеют код 3 и работники со сдельной опла¬ той труда имеют 4. Используйте оператор switch для расчета код в зависимости от его кода. Внутри оператора switch попросите пользователя (т.е. клерка, рассчитываю¬ щего зарплату) ввести соответствующие данные, которые потребу¬ зарплаты каждого служащего вашей программе для расчета зарплаты каждого служащего, исходя из его кода оплаты. ются 4.29. (Законы де Моргана.) В этой главе мы обсуждали логические опера¬ !. Законы де Моргана иногда могут помочь нам запи¬ ции &&, сать логическое выражение в более удобной форме. Эти законы уста¬ и \(условие1 && условие2) логически (\условие1 \условие2). Также выраже¬ ние \(условие1 логически эквивалентно выражению условие2) && законы де Моргана для напи¬ (\условие1 \условие2). Используйте что навливают, выражение эквивалентно выражению сания эквивалентного выражения для каждого из следующих выра¬ жений, а затем напишите программу, показывающую, что исходное и новое выражения в каждом случае являются эквивалентными: a) ! (x < b) ! (а F= c) ! ( (x d) ! (i 8) | | 4) >= ! (у ! (g | | b) <= > && 5) && (Y (j 7) != 5) > <= 4) ) б)) программу на рис. 4.7, заменив оператор switch вло¬ женным оператором if/else; будьте внимательны и надлежащим об¬ 4.30.Перепишите разом рассмотрите блок default. Затем перепишите эту новую вер¬ сию, заменяя вложенные операторы if/else серией операторов if; здесь также будьте внимательны default (это сделать труднее, чем и рассмотрите в варианте Упражнение показывает, что оператор switch удобства и что любой такой оператор может помощью операторов с одним выбором. 4.31.Напишите ромба. Вы с реализацию вложенным блока if/else). введен в сущности для быть записан только с программу, которая выводит следующую фигуру в виде printf, которые выводят (*), либо один пробел. Максимизируйте количе¬ ство используемых вами повторений (посредством вложенных структур for) и минимизируйте число операторов printf. можете использовать операторы либо одну звездочку
183 Управление программой * * ***** ******* ********* ******* ***** *** * 4.32.Измените программу, которую вы написали в упражнении 4.31 так, чтобы она считывала нечетное число в диапазоне от 1 до 19 для зада¬ ния количества бражать 4.33. Если на вы знакомы с ромб ваша программа должна ото¬ соответствующего размера. римскими цифрами, напишите программу, кото¬ рая выводит таблицу в от диапазоне ромбе. Затем в строк экране всех римских эквивалентов десятичных чисел 1 до 100. 4.34. Напишите программу, которая выводит таблицу двоичных, и ричных эквивалентов шестнадцатеричных десятичных восьме¬ чисел в 1 до 256. Если вы не знакомы с этими системами счис¬ хотели бы попытаться сделать это упражнение, сначала про¬ диапазоне от ления и читайте приложение Д. 4.35.0пишите do/while процесс, который потребовался бы для замены цикла while. Какая возникает проб¬ эквивалентным ему циклом лема, когда вы пытаетесь заменить цикл while эквивалентным цик¬ лом do/while? Предположим, что цикл while и заменить его циклом вам бы ее использовали, ведет программа себя так же, как нужно убрать дополнительная понадобиться чтобы гарантировать, точно что do/while. Какая управляющая структура могла бы вам зом вы сказали, и каким обра¬ что получившаяся оригинал. 4.36.Напишите программу, которая вводит год в диапазоне от 1994 до 1999 и использует повторение с циклом for для печати сжатого, ак¬ куратного календаря. Обратите внимание на високосные годы. 4.37.0ператоры break является всегда и continue критикуют за то, что каждый из них неструктурным. В самом деле, операторы break и continue можно заменить структурными операторами, хотя выглядеть это может неуклюже. могли бы убрать Опишите из цикла в общем случае, каким любой оператор break образом вы и заменить его не¬ которым структурным эквивалентом. (Подсказка: при помощи опе¬ ратора break мы выходим из цикла, находясь внутри тела цикла. Другим способом выхода является отрицательный результат провер¬ Рассмотрите использование при проверке второй проверки, которая указывает на «до¬ ки на продолжение цикла. на продолжение цикла срочный выход из-за условия *break ».) Примените разработанный здесь оператора рис. метод для устранения break 4.11. 4.38.Что делает следующий фрагмент программы? из программы на
184 Глава 4 for (i for 1; = (j for = j <= 5; 1; j <= i++) { 3; j++) 1, к <= printf ("*"); printf ("\n"); (к = 4; { к++) } printf ("\n"); } 4.39. Опишите цикла ным в общем случае, каким любой оператор continue эквивалентом. образом вы и заменить его могли бы убрать из некоторым структур¬ Примените разработанный здесь метод для устранения оператора continue из программы на рис. 4.12. 4.40. Опишите в общем случае, каким образом торы break из структуры валентами. Примените разработанный люжий) switch вы могли и заменить их бы убрать опера¬ структурными экви¬ здесь метод (возможно неук¬ для устранения операторов break из программы на рис. 4.7.
г л в а а 5 Функции Цели Понять принципы модульного построения программ из небольших блоков, называемых функциями. Познакомиться с часто используемыми математиче¬ скими функциями стандартной библиотеки С. Научиться созданию новых Понять механизмы обмена функций. информацией между функциями. Познакомиться ванными Понять ций, на с принципы моделирования, методиками генерации случайных написания и вызывающих самих себя. осно¬ чисел. использования функ¬
186 Глава 5 Содержание 5.1. Введение 5.2. Программные модули 5.3. Функции 5.4. Функции 5.5. Определения функций Прототипы функций 5.7. Заголовочные 5.9. файлы функций: вызов Генерация случайных 5.10.Пример: 5.11. Классы С математической библиотеки 5.6. 5.8. Вызов в по значению и по ссылке чисел стохастическая игра памяти 5.12.Правила области действия 5.13. Рекурсия 5.14.Пример применения рекурсии: 5.15.Рекурсия Резюме в сравнении ошибки программирования Советы по переносимости программ эффективности самоконтроля Фибоначчи итерацией Распространенные программирования шению с числа Ответы Общие методические на упражнения 5.1. замечания для самоконтроля Хороший Советы стиль по повы¬ Упражнения для Упражнения Введение Большинство компьютерных программ, которые решают задачи реально¬ больше тех, что представлены в первых главах этой книги. Как показывает практика, наилучшим способом разработки и поддержки бо¬ го мира, намного льших программ является конструирование программы из небольших частей, или модулей, с каждым из которых обращаться проще, чем с первоначальной программой. Эта реализацию, «разделяй и властвуй». Данная С, которые упрощают проектирование, методика следует принципу глава описывает возможности языка использование и сопровождение больших программ.
Функции 187 5.2. Модули в С Программные модули в С функциями. Программы на С обычно пишутся пу¬ функций, созданных программистом, с функциями, ко¬ называются тем соединения новых стандартной библиотеки С. В этой главе мы обсудим оба вида функций. Стандартная библиотека С предоставляет широ¬ кий набор функций для выполнения общих математических вычислений, об¬ работки строк и символов, ввода/вывода и многих других полезных операций. Стандартные функции упрощают работу программиста, поскольку удовлетво¬ ряют многим из его потребностей. торые поставляются в составе Хороший Освойтесь стиль программирования 5.1 с широким набором функций стандартной библиотеки ANSI С. Общее методическое замечание 5.1 Не изобретайте колесо. Если есть возможность, используйте функции стандартной библиотеки ANSI С вместо того, чтобы создавать новые функции Это уменьшает время разработки программы Совет по переносимости программ 5.1 Использование функций стандартной библиотеки ANSI С делает программы более пе¬ реносимыми. Хотя языка scanf С, функции стандартной библиотеки технически не являются частью они неизменно поставляются с системами: и pow, которые ANSI С. Функции printf, мы использовали в предыдущих главах, являются функ¬ стандартной библиотеки. Программист может написать функции циями для выделения конкретных задач, которые могут вызываться из многих точек программы. Эти функции иногда упоминаются как функции, определенные программистом. Операторы, опре¬ деляющие функцию, пишутся только один раз, и эти операторы скрыты от функций. Обращение к функции осуществляется посредством вызова функции. В вызове функции указывается ее имя и передается информация (в качестве ар¬ гументов), необходимая функции для выполнения своей задачи. Аналогом та¬ других форма управления. Начальник (вызы¬ вающая функция) просит работника (вызываемую функцию) выполнить зада¬ кой процедуры служит иерархическая будет выполнено, сообщить об этом. Например, функция, ко¬ торой требуется вывести информацию на экран, вызывает для выполнения за¬ дачи рабочую функцию printf; printf выводит информацию и передает (т.е. возвращает) вызывающей функции сообщение о выполнении своей задачи. Функция-начальник не знает, каким образом функция-работник выполняет поставленную задачу. Работник может вызывать другие рабочие функции, а начальник не будет об этом знать. Мы скоро увидим, как это «сокрытие» по¬ дробностей выполнения задачи способствует разработке хороших программ. ние и, когда оно На рис. 5.1 показана функция main, поддерживающая связь с несколькими рабочими функциями согласно законам иерархии. Обратите внимание, что действует как функция-начальник по отношению к worker4 и wor- workerl ker5. Отношения между функциями могут ную от иерархической, показанной иметь и другую структуру, отлич¬ на этом рисунке.
Глава 5 188 main workerl worker2 worker4 worker3 worker5 Рис. 5.1. Иерархия отношений функции-начальника и функции-работника Функции математической библиотеки 5.3. Функции математической библиотеки позволяют программисту выпол¬ общие математические вычисления. Мы воспользуемся раз¬ личными функциями математической библиотеки, чтобы познакомиться с концепцией функций. Позже в книге мы обсудим многие другие функции стандартной библиотеки С. Полный список функций стандартной библиотеки приведен в приложении Б. Обычно к функции обращаются, записывая имя с последующими левой нять некоторые круглой скобкой, аргументом функции (или списком аргументов, отделяе¬ мых друг от друга запятыми) и желающий вычислить 900.0, мог бы написать мист, pnntf("%.2f, правой круглой скобкой. Например, и напечатать значение програм¬ квадратного корня из sqrt(900.0)); Когда этот оператор выполняется, для вычисления значения квадратного корня из числа в скобках (900.0) производится вызов функции тической библиотеки. Число 900.0 является аргументом Показанный оператор типа double и напечатал возвращает бы 30.00. Функция sqrt получает аргумент типа double. Все функции математиче¬ результат ской библиотеки возвращают данные При типа double. стиль программирования 5.2 Хороший использовании ловочный sqrt из матема¬ функции sqrt. файл функций математической библиотеки включите в программу заго¬ математики с помощью директивы препроцессора #include <math.h> Распространенная ошибка программирования 5.1 При отсутствии в программе заголовочного файла математики обращение программы функциям математической библиотеки может приводить к странным результатам к Аргументы функций ниями. Если cl = 13.0, d printf("%.2f", вычисляет т.е. 5.00. и могут быть константами, переменными или выраже¬ = sqrt(cl печатае^ ' 3.0 + и d значение f * = 4.0, f) ) то оператор ; квадратного кОрня из 13.0+3.0*4.0=25.0,
189 Функции Некоторые функции математической библиотеки С Переменные x и у имеют тип Функция Назначение sqrt(x) квадратный корень exp(x) экспоненциальная log(x) натуральный логарифм log10(x) логарифм fabs(x) абсолютное x Пример sqrt(900.0) равен 30 sqrt(9.0) равен 3.0 из x функция ех x (основание e) log(2 718282) равен 1 0 log(7 389056) равен 2 0 (основание 10) log10(1 0) равен 0 0 log10(10 0) равен 1 0 log10(100 0) равен 2 0 значение x если x > если округляет x до округляет x до не ceil(9 2) равно 10 0 ceil(-9.8) равно -9.0 ближайшего целого, не floor(9.2) равно 9 0 floor(-9.8) равно -10 0 превосходящего x pow(x, у) fmod(x, x возводится в степень у у) остаток х/у как число с (ху) pow(2, 7) равно 128 0 pow(9, 5) равно 3 0 плавающей точкой (x sin(x) тригонометрический синус cos(x) тригонометрический косинус x (x в tan(x) тригонометрический тангенс x (x в Рис. 5.2. Функции общего x они шинство функций ям sin(0 0) равен 0 0 радианах) cos(0.0) равен 1 0 радианах) tan(0 0) равен 0.0 радианах) библиотеки Функции разбить программу на модули. Все пе¬ функций, являются локальными пе¬ известны только той функции, в которой определены. Боль¬ имеют список параметров. Параметры позволяют функци¬ позволяют программисту ременные, объявленные ременными в fmod(13 657, 2 333) равен 1 992 назначения математической 5.4. Функции 0, то fabs(x) равно x x=0, то fabs(x) равно 0.0 x < 0, то fabs(x) равно -x ближайшего целого, меньшего x floor(x) 0 exp(1 0) равна 2 718282 exp(2 0) равна 7.389056 если ceil(x) приведены на рис. 5.2. double. в определениях обмениваться информацией. Параметры функции это также локальные переменные. Общее методическое замечание 5.2 В программах, содержащих большое число как группа обращений Существует несколько Другой функциям, функций, main должна быть реализована выполняющим основную часть оснований для разбиения программы работы. на функции. властвуй» разработку программы более контроли¬ мотив это повторное использование кода, т.е. использова¬ Подход «разделяй руемой. к и делает
190 Глава 5 ние однажды написанных функций в качестве конструктивных блоков для со¬ Многократное использование кода является основ¬ фактором при переходе к объектно-ориентированному здания новых программ. ным определяющим программированию. Имея хорошо продуманные имена и определения функ¬ ций, можно создавать программы из стандартизованных модулей, не создавая специализированного ракция. программного Мы прибегаем вызывающую функции кода. Эта методика известна как абст¬ абстракции всякий раз, когда пишем программу, стандартной библиотеки, например, printf, scanf и к Третьей причиной является правило избегать дублирования кода в про¬ Оформление кода в виде функции позволяет выполнять его в разных частях программы посредством простого вызова функции. pow. грамме. Общее методическое замечание 5.3 Каждая функция должна ограничиваться выполнением одной, точно определенной задачи, а имя функции должно отражать смысл данной задачи Это облегчает абст¬ ракцию и способствует многократному использованию программного кода Общее методическое замечание 5.4 Если вы не можете выбрать краткое имя, которое выражает назначение функции, воз¬ можно, что ваша функция пытается выполнить слишком много задач. Лучше разбить такую функцию на несколько меньших функций. 5.5. Определения функций Каждая из приведенных ранее программ состояла из функции с именем main, которая для выполнения своей задачи вызывала функции стандартной библиотеки. Теперь мы рассмотрим, как программисты пишут свои собствен¬ ные специализированные функции. Рассмотрим программу, которая использует функцию square для вычис¬ ления квадрата целых чисел от 1 до 10 (рис. 5.3). Функция square активируется, или вызывается в main внутри операто¬ ра printf: pnntf("%d ", square(x)); Функция square получает копию значения x в параметре у. Затем square y*y. Результат передается назад square, и printf выводит результат. вычисляет значение произведения printf в main, где была вызвана функции Этот про¬ цесс повторяется десять раз в цикле for. Определение функции square показывает, что square предполагает пере¬ дачу в параметре у целого значения. Ключевое слово int, предшествующее имени функции, показывает, что square возвращает результат целого типа. Оператор return в square передает результат вычислений функции, которая вызвала square. Строка int программы square(mt); является прототипом функции. Ключевое слово int в скобках информирует вызывающей функции целое значение. Слово int слева от имени функции square информирует компи¬ лятор о том, что square возвращает вызывающей функции результат целого компилятор о том, что square предполагает получить от
191 Функции Компилятор использует прототип функции для проверки того, что вызо¬ корректный тип возвращаемых данных, корректное число ар¬ гументов, корректный тип аргументов и что аргументы следуют в правильном порядке. Прототипы функций детально рассматриваются в разделе 5.6. типа. вы square имеют /* Функция square, #include <stdio.h> определенная int /* sguare(mt); прототип программистом */ функции */ main () { int x; for (x 1; = x 10; <= ", printf("%d x++) square(x)); printf("\n"); return 0; } /* Определение функции */ int square(mt у) { return у * у; } 4 1 16 9 25 36 49 Рис. 5.3. Использование Хороший стиль 64 81 100 функции, определенной программистом программирования 5.3 Вставляйте пустую строку между определениями функций, чтобы отделить функции друг от друга и улучшить Определение функции тем самым имеет читаемость программы следующий формат: тип_возвращаемого_значения имя_функции(список_параметров) { объявления операторы } В качестве имени функции (имя_функции) может использоваться любой допустимый идентификатор. Типом результата, возвращаемого вызывающей функции, является тип_возвращаемого_значения. Если тип_возвращаемого_значения задан ключевым словом void, то это означает, что функция ниче¬ го не возвращает. Если тип_возвращаемого_значения не указан, то компиля¬ тор будет предполагать тип int. Распространенная ошибка программирования 5.2 Если тип_возвращаемого_значения в определении функции опущен, то это вызовет синтаксическую ошибку в том случае, если прототип функции определяет, что возвра¬ щаемый тип не является целым (int)
192 Глава 5 Распространенная ошибка программирования 5.3 Если функция должна возвращать некоторое значение, а в функции опущен оператор возврата, то это может привести к непредвиденным ошибкам Стандарт ANSI гласит, что в этом случае результат не определен Распространенная ошибка программирования 5.4 Возврат значения из функции, для которой void, вызывает Хороший Хотя синтаксическую возвращаемый тип был объявлен как ошибку. стиль программирования 5.4 опущенный возвращаемый int, всегда за¬ функции main возвращае¬ тип по умолчанию расценивается как давайте возвращаемый тип явным образом Однако мый тип обычно опускается для это список объявлений параметров (отделенных Список_параметров запятыми), получаемых функцией при ее вызове. Если функция не получает значения, список_параметров обозначается ключевым словом void. Тип каж¬ дого параметра должен int. Если быть указан явно за исключением параметров тип параметра не указан, то по умолчанию принимается тип типа int. Распространенная ошибка программирования 5.5 Запись float x, у вместо щихся к одному типу ло бы параметру у float x, float у при объявлении параметров функции, относя¬ объявление параметров, как float x, у фактически присвои¬ Такое тип int, поскольку int принимается по умолчанию Распространенная ошибка программирования 5.6 Символ точки ров в с запятой после определении функции, правой круглой скобки, закрывающей является синтаксической список парамет¬ ошибкой Распространенная ошибка программирования 5.7 Повторное определение параметра функции синтаксическая ошибка ции как локальной переменной внутри функ¬ - Хороший стиль программирования 5.5 Включайте тип каждого параметра в список параметров функции даже в том случае, если данный параметр относится к типу int, принятому по умолчанию Хороший стиль программирования 5.6 ошибкой, не используйте одни и те функции аргументов и соответствующих параметров Хотя это и не является же имена для передавае¬ мых в Это поможет Объявления ции. Тело определении функции. избежать неоднозначности операторы внутри фигурных скобок это функции также называют блоком. Блок и образуют просто тело функ¬ составной опе¬
193 Функции который включает в себя объявления. Переменные могут быть объявле¬ любом блоке, а блоки могут быть вложены. В любом случае функция не может быть определена внутри другой функции. ратор, ны в Распространенная ошибка программирования 5.8 - Определение функции внутри другой функции Хороший Выбор Общее ошибка стиль программирования 5.7 осмысленных имен таемыми синтаксическая и помогает функций и параметров делает программы избежать чрезмерного использования более удобочи¬ комментариев. методическое замечание 5.5 функции не должна превышать одной страницы. Еще лучше, чтобы функция не превышала по длине половины страницы. Небольшие функции способствуют повтор¬ ному использованию программного кода. Длина Общее методическое замечание 5.6 Программы должны составляться в виде совокупности небольших функций. упрощает создание программ, их отладку, поддержку и модификацию. Это Общее методическое замечание 5.7 Функция, требующая большого количества параметров, может выполнять слишком много задач. Рассмотрите возможность ее разделения на меньшие функции, которые выполняют выделенные задачи. Заголовок функции должен, по возможности, умеща¬ ться на одной строке. Общее методическое замечание 5.8 Прототип функции, заголовок функции и вызов функции должны иметь взаимное со¬ ответствие по числу, типу и порядку следования аргументов и параметров, а также по типу возвращаемого значения. Существует три способа возвращения управления в ту точку программы, в которой была вызвана функция. Если функция не возвращает результат, управление возвращается просто при достижении правой фигурной скобки, завершающей функцию, или при исполнении оператора return; Если функция возвращает результат, оператор return выражение; возвращает вызывающей функции значение выражения. Наш второй пример демонстрирует определенную программистом функ¬ цию maximum для определения и возврата наибольшего из трех целых значе¬ ний (рис. 5.4). С помощью функции scanf три целых числа вводятся. После этого числа передаются функции maximum, которая находит наибольшее це¬ лое число. Это значение возвращается в main оператором return в функции maximum. Возвращенное 7 Зак 801 значение выводится на печать оператором printf.
194 Глава 5 /* Поиск максимального int из целых трех значений maximum(int, main () { int а, b, int, int); /* прототип функции */ с; printf("Enter three integers: "); scanf("%d%d%d", &a, &b, &c); printf("Maximum is: %d\n", maximum(a, return /* int */ <stdio.h> #include b, c)); 0; функции Определение maximum(int x, int maximum у, int */ z) { int if max > (у > (z max return x; max) = max if = у; max) = z; max; integers: 85 22 85 17 integers: 85 85 22 17 Enter three integers: Maximum is: 85 22 17 85 Enter three Maximum is: Enter three Maximum is: Рис. 5.4. Определенная программистом функция maximum 5.6. Одной наиболее из функций. Они были Прототипы функций важных особенностей ANSI С позаимствованы комитетом Прототип функции сообщает компилятору являются прототипы ANSI С у разработчиков С++. тип данных, возвращаемых функ¬ цией, число параметров, получаемых функцией, тип и порядок следования па¬ раметров. Компилятор использует прототипы функций для проверки коррект¬ ности обращений к функции. Предыдущие версии С не выполняли этот вид проверки, так что существовала возможность неправильного вызова функции, при котором компилятор не регистрировал ошибки. Такие обращения могли приводить к фатальным ошибкам при кам, которые, выполнении программы, или к ошиб¬ не вызывая краха программы, приводили тем не менее к труд- нообнаружимым логическим ляют такое положение дел. ошибкам. Прототипы функций в ANSI С исправ¬
195 Функции Хороший стиль программирования 5.8 Включайте в программу прототипы для всех функций, чтобы воспользоваться преи¬ муществами проверки типов С Используйте директивы препроцессора #include, что¬ бы получить прототипы функций стандартной библиотеки m заголовочных файлов соответствующих библиотек Также используйте #include, чтобы включить заголовоч¬ ные файлы, содержащие ваши собственные прототипы функций или прототипы, ис¬ пользуемые членами вашей рабочей группы Прототипом функции maximum, приведенной mt maximum(mt, что возвращает результат типа int. имеет тот же вид, что и 5.4, первая maximum получает три параметра Обратите строка Имена ния типа int внимание, что прототип определения функции функции maximum, за исключением того, что в него не включены имена параметров Хороший является int); int, Этот прототип объявляет, и на рис. (x, у и z). стиль программирования 5.9 параметров иногда включаются Компилятор игнорирует эти в прототип функции с целью документирова¬ имена Распространенная ошибка программирования 5.9 Пропуск точки с запятой в конце прототипа функции вызывает синтаксическую ошибку Вызов функции, который не соответствует ее прототипу, вызывает син¬ ошибку. Ошибка генерируется также в том случае, если не согла¬ сованы прототип функции и ее функции. Например, если прототип функции записать в виде (рис. 5.4) таксическую void maximum(mt, mt, int) ; ошибку, поскольку возврат типа void в про¬ тотипе функции отличался бы от возврата типа int заголовка функции. то компилятор зарегистрировал бы автомати¬ Другое важное следствие применения прототипов функций приведение аргументов, т.е. принудительное преобразование аргумен¬ тов функции к соответствующему типу. Например, функция математической библиотеки sqrt может вызываться с целочисленным аргументом, и функция будет работать правильно, хотя прототип ее в math.h определяет аргумент как ческое double. Оператор pnntf("%.3f\n", sqrt(4)); правильно вычисляет sqrt(4) и выводит на печать значение 2.000. Прототип функции заставляет компилятор преобразовать целое значение 4 в значение типа double 4.0 перед тем, как значение будет передано sqrt. В общем слу¬ чае значения аргументов, которые не соответствуют в точности типам парамет¬ ров в прототипе функции, перед вызовом преобразуются в соответствующий тип. Эти преобразования могут привести учитывать правила возведения типов С. к неправильным результатам, если не Правила возведения определяют, ка¬ ким образом одни типы могут быть преобразованы в другие типы без потери данных. В нашем примере с sqrt тип int автоматически был преобразован в do¬ uble без изменения значения. Однако преобразование типа double в тип int от¬ брасывает дробную часть значения double. Преобразование типа больших целых к типу малых целых (например, long в short) может также приводить к изменению 7* значений.
Глава 5 196 Правила возведения автоматически применяются к выражениям, содер¬ жащим значения двух или более типов данных (называемым выражениями смешанного типа). Каждое значение в выражении смешанного типа автома¬ тически возводится к наивысшему типу выражения1 (фактически в выраже¬ временная версия каждого значения, а первона¬ чальные значения остаются неизменными). На рис. 5.5 перечислены типы нии создается и используется данных в порядке от наивысшего к низшему и приведены спецификации пре¬ образования для операторов printf и scanf. Тип данных спецификация преобразования для printf спецификация преобразования для scanf long double %Lf %Lf double %f %lf float %f %f unsigned long int %lu %lu long int %ld %ld unsigned int %u %u int %d %d short %hd %hd char %c %c Рис. 5.5. Иерархия возведения Преобразование значений ному результату. низкий к более Следовательно, тип только посредством явного присваивания преобразуются они обычно приводит к невер¬ быть преобразовано в более низким типам значение может го типа или с помощью операции приведения. то типов данных к типу параметров прототипа непосредственно присваиваются переменной более низко¬ Значения аргументов функции функции переменным таким этого образом, типа. как Если буд¬ наша функция square, которая имеет параметр целого типа (рис. 5.3), вызывается с аргументом типа float, то аргумент преобразуется в тип int (более низкий тип). В этом случае square может вернуть неверное значение. re(4.5) дало бы значение 16, а не Например, squa- более низко¬ 20.25. Распространенная ошибка программирования 5.10 Преобразование более му типу может высокого типа данных в иерархии возведения к изменить значение данных. Если прототип для данной функции лятор дение 1 не был включен в программу, компи¬ формирует собственный прототип функции, используя ее первое вхож-' в программу: или определение, или обращение к функции. По умолча¬ Точнее говоря, процедура возведения типа применяется последовательно к каждой паре операндов текущей операции по ходу оценки выражения. Например, оценка выражения 3/2*3.14 даст врезультате 3.14, так как для первой операции (/) никакого возведения не производится (оба операнда целые) и результатом ее будет целое 1. Только на следующем шаге этот промежуточный результат будет возведен до 1.0, поскольку второй операнд умно¬ жения число с плавающей точкой. Прим. ред.
197 Функции нию компилятор предполагает, что функция возвращает тип int, ничего не Следовательно, если аргументы, функции, некорректны, то компилятор не обнаружит ошибку. предполагая относительно типа аргументов. переданные Распространенная ошибка программирования 5.11 Пропуск прототипа функции вызывает синтаксическую ошибку, щает тип, отличный от int, а определение зова во функции появляется функции. В других случаях пропуск прототипа функции время выполнения программы или привести к если функция возвра¬ в программе после вы¬ может вызвать ошибку непредвиденному результату Общее методическое замечание 5.9 Прототип функции, размещенный вне любого определения функции, применяется ко всем вызовам, появляющимся в файле после прототипа функции. Прототип функции, помещенный в функцию, применяется функции. только к тем вызовам, которые делаются из этой 5.7. Заголовочные файлы Каждая стандартная библиотека имеет свой заголовочный файл, содержа¬ всех функций данной библиотеки, а также определения и констант, необходимых этим функциям. На различных типов данных 5.6 список заголовочных рис. приведен алфавитный файлов стандартной биб¬ лиотеки, которые могут быть включены в программу. Термин «макрос», кото¬ рый несколько раз используется в рис. 5.6, подробно обсуждается в главе 13, щий прототипы для « Препроцессор ». Заголовочный файл стандартной библиотеки Содержимое <assert.h> Содержит заголовочного файла диагностические макросы и информацию, которая помогает при отладке программы <ctype.h> Содержит прототипы для функций, которые проверяют определенные характеристики символов и прототипы для функций, которые могут использоваться для преобразования символов и наоборот нижнего регистра в символы верхнего регистра сообщений ошибки <errno.h> Определяет макросы, которые <float.h> Содержит пределы значений для чисел <limits.h> Содержит пределы значений <locale.h> функций и другую информацию, которая позволяет изменять работу программы согласно правилам текущего локала, в котором она выполняется Понятие локала дает компьютерной системе возможность обрабатывать соглашения, полезны для с о типе плавающей точкой в системе для целых чисел в системе. Содержит прототипы принятые в различных странах по всему миру для выражения таких данных, как дата, время, денежные единицы и способ записи больших чисел. <math.h> <setjmy.h> Содержит прототипы для функций математической библиотеки Содержит прототипы для функций, которые позволяют обойти вызов функции и последовательность возврата обычный
Глава 5 198 Заголовочный файл стандартной библиотеки Содержимое <signal.h> Содержит прототипы фун