Содержание.
Аннотация
0.1. Введение
1. Учебное введение
1.1. Начинаем
1.2. Переменные и арифметика
1.3. Оператор FOR
1.4. Символические константы
1.5. Набор полезных программ
1.5.1. Ввод и вывод символов
1.5.2. Копирование файла
1.5.3. Подсчет символов
1.5.4. Подсчет строк
1.5.5. Подсчет слов
1.6. Массивы
1.7. Функции
1.8. Аргументы - вызов по значению
1.9. Массивы символов
1.10. Область действия: внешние переменные
1.11. Резюме
2. Типы, операции и выражения
2.1. Имена переменных
2.2. Типы и размеры данных
2.3. Константы
2.3.1. Символьная константа
2.3.2. Константное выражение
2.3.3. Строчная константа
2.4. Описания
2.5. Арифметические операции
2.6. Операции отношения и логические операции
2.7. Преобразование типов
2.8. Операции увеличения и уменьшения
2.9. Побитовые логические операции
2.10. Операции и выражения присваивания
2.11. Условные выражения
2.12. Старшинство и порядок вычисления
3. Поток управления
3.1. Операторы и блоки
3.2. IF - ELSE
3.3. ELSE - IF
3.4. Переключатель
3.5. Циклы - WHILE и FOR
3.6. Цикл DO - WHILE
3.7. Оператор BREAK
3.8. Оператор CONTINUE
3.9. Оператор GOTO и метки
4. Функции и структура программ
4.1. Основные сведения
4.2. Функции, возвращающие нецелые значения
4.3. Еще об аргументах функций
4.4. Внешние переменные
4.5. Правила, определяющие область действия
4.5.1. Область действия
4.6. Статические переменные
4.7. Регистровые переменные
4.8. Блочная структура
4.9. Инициализация
4.10. Рекурсия
4.11. Препроцессор языка “C”
4.11.1. Включение файлов
4.11.2. Mакроподстановка
5. Указатели и массивы
5.1. Указатели и адреса
5.2. Указатели и аргументы функций
5.3. указатели и массивы
5.4. Адресная арифметика
5.5. указатели символов и функции
5.6. Указатели - не целые
5.7. Многомерные массивы
5.8. Массивы указателей; указатели указателей
5.9. Инициализация массивов указателей
5.10. Указатели и многомерные массивы
5.11. Командная строка аргументов
5.12. Указатели на функции
6. Структуры
6.1. Основные сведения
6.2. Структуры и функции
6.3. Массивы структур
6.4. Указатели на структуры
6.5. Структуры, ссылающиеся на себя
6.6. Поиск в таблице
6.7. Поля
6.8. Объединения
6.9. Определение типа
7. Ввод и вывод
7.1. Обращение к стандартной библиотеке
7.2. Стандартный ввод и вывод - функции GETCHAR и PUTCHAR
7.3. Форматный вывод - функция PRINTF
7.4. Форматный ввод - функция SCANF
7.5. Форматное преобразование в памяти
7.6. Доступ к файлам
7.7. Обработка ошибок - STDERR и EXIT
7.8. Ввод и вывод строк
7.9. Несколько разнообразных функций
7.9.1. Проверка вида символов и преобразования
7.9.2. Функция UNGETC
7.9.3. Обращение к системе
7.9.4. Управление памятью
8. Интерфейс системы UNIX
8.1. Дескрипторы файлов
8.2. Низкоуровневый ввод/вывод - операторы READ и WRITE
8.3. Открытие, создание, закрытие и расщепление (UNLINK)
8.4. Произвольный доступ - SEEK и LSEEK
8.5. Пример - реализация функций FOPEN и GETC
8.6. Пример - распечатка справочников
8.7. Пример - распределитель памяти
9. Приложение а: справочное руководство по языку 'C'.
9.1. Введение
10. Лексические соглашения
10.1. Комментарии
10.2. Идентификаторы (имена)
10.3. Ключевые слова
10.4. Константы
10.4.1. Целые константы
10.4.2. Явные длинные константы
10.4.3. Символьные константы
10.4.4. Плавающие константы
10.5. Строки
10.6. Характеристики аппаратных средств
11. Синтаксическая нотация
12. Что в имени тебе моем?
13. Объекты и L-значения
14. Преобразования
14.1. Символы и целые
14.2. Типы FLOAT и DOUBLE
14.3. Плавающие и целочисленные величины
14.4. Указатели и целые
14.5. Целое без знака
14.6. Арифметические преобразования
15. Выражения
15.1. Первичные выражения
15.2. Унарные операции
15.3. Мультипликативные операции
15.4. Аддитивные операции
15.5. Операции сдвига
15.6. Операции отношения
15.7. Операции равенства
15.8. Побитовая операция 'и'
15.9. Побитовая операция исключающего 'или'
15.10. Побитовая операция включающего 'или'
15.11. Логическая операция 'и'
15.12. Операция логического 'или'
15.13. Условная операция
15.14. Операция присваивания
15.15. Операция запятая
16. Описания
16.1. Спецификаторы класса памяти
16.2. Спецификаторы типа
16.3. Описатели
16.4. Смысл описателей
16.5. Описание структур и объединений
16.6. Инициализация
16.7. Имена типов
16.8. TYPEDEF
17. Операторы
17.1. Операторное выражение
17.2. Составной оператор (или блок)
17.3. Условные операторы
17.4. Оператор WHILE
17.5. Оператор DO
17.6. Оператор FOR
17.7. Оператор SWITCH
17.8. Оператор BREAK
17.9. Оператор CONTINUE
17.10. Оператор возврата
17.11. Оператор GOTO
17.12. Помеченный оператор
17.13. Пустой оператор
18. Внешние определения
18.1. Внешнее определение функции
18.2. Внешние определения данных
19. Правила, определяющие область действия
19.1. Лексическая область действия
19.2. Область действия внешних идентификаторов
20. Строки управления компилятором
20.1. Замена лексем
20.2. Включение файлов
20.3. Условная компиляция
21. Неявные описания
22. Снова о типах
22.1. Структуры и объединения
22.2. Функции
22.3. Массивы, указатели и индексация
22.4. Явные преобразования указателей
23. Константные выражения
24. Соображения о переносимости
25. Анахронизмы
26. Сводка синтаксических правил
26.1. Выражения
26.2. Описания
26.3. Операторы
26.4. Внешние определения
26.5. Препроцессор
27. Присваивание структуры
28. Тип перечисления
29. Таблица изображений непечатных символов языка “C”.
Аннотация. Язык “C”(произносится “си”) - это универсальный язык программирования,
для которого характерны экономичность выражения, современный поток управления
и структуры данных, богатый набор операторов. Язык “C” не является ни языком “очень
высокого уровня”, ни “большим” языком, и не предназначается для некоторой
специальной области применения. но отсутствие ограничений и общность языка делают
его более удобным и эффективным для многих задач, чем языки, предположительно
более мощные. Язык “C”, первоначально предназначавшийся для написания операционной
системы “UNIX” на ЭВМ DEC PDP-11, был разработан и реализован на этой системе
Деннисом Ричи. Операционная система, компилятор с языка “C” и по существу
все прикладные программы системы “UNIX” (включая все программное обеспечение, использованное
при подготовке этой книги) написаны на “C”. Коммерческие компиляторы
с языка “C” существуют также на некоторых других 0.1. Введение. Язык “C” является
универсальным языком программирования. Он тесно связан с операционной системой
“UNIX” , так как был развит на этой системе и так как “UNIX” и ее программное
обеспечение написано на “C”. Сам язык , однако, несвязан с какой-либо одной
операционной системой или машиной;и хотя его называют языком системного программирования,
таккак он удобен для написания операционных систем, он с равнымуспехом
использовался при написании больших вычислительныхпрограмм, программ для
обработки текстов и баз данных.Язык “C” - это язык относительно “низкого уровня”.
Втакой характеристике нет ничего оскорбительного; это простоозначает, что “C”
имеет дело с объектами того же вида, что ибольшинство ЭВМ, а именно, с символами,
числами и адресами.Они могут объединяться и пересылаться посредством обычныхарифметических
и логических операций, осуществляемых реаль-ными ЭВМ.В языке “C”
отсутствуют операции, имеющие дело непос-редственно с составными объектами,
такими как строки симво-лов, множества, списки или с массивами, рассматриваемыми
какцелое. Здесь, например, нет никакого аналога операциям PL/1,оперирующим с
целыми массивами и строками. Язык не предос-тавляет никаких других возможностей
распределения памяти,кроме статического определения и механизма стеков, обеспечи-ваемого
локальными переменных функций; здесь нет ни“куч”(HEAP), ни “сборки мусора”,
как это предусматривается вАЛГОЛЕ-68. Наконец, сам по себе “C” не обеспечивает
никакихвозможностей ввода-вывода: здесь нет операторов READ илиWRITE и
никаких встроенных методов доступа к файлам. Все этимеханизмы высокого уровня должны
обеспечиваться явно вызыва-емыми функциями.Аналогично, язык “C” предлагает
только простые, после-довательные конструкции потоков управления: проверки, циклы,группирование
и подпрограммы, но не мультипрограммирование,параллельные операции,
синхронизацию или сопрограммы.Хотя отсутствие некоторых из этих средств
может выгля-деть как удручающая неполноценность (“выходит, что я долженобращаться
к функции, чтобы сравнить две строки символов?!”), но удержание языка в скромных
размерах дает реальныепреимущества. Так как “C” относительно мал, он не требуетмного
места для своего описания и может быть быстро выучен.Компилятор с “C”
может быть простым и компактным. Кроме то-го, компиляторы легко пишутся; при
использовании современнойтехнологии можно ожидать написания компилятора для новой
ЭВМза пару месяцев и при этом окажется, что 80 процентов прог-раммы нового
компилятора будет общей с программой для ужесуществующих компиляторов. Это обеспечивает
высокую степеньмобильности языка. Поскольку типы данных и стуктуры управле-ния,
имеющиеся в “C”, непосредственно поддерживаются боль-шинством существующих
ЭВМ, библиотека, необходимая во времяпрогона изолированных программ, оказывается
очень маленькой.На PDP -11, например, она содержит только программы для32-битового
умножения и деления и для выполнения программввода и вывода последовательностей.
Конечно, каждая реализа-ция обеспечивает исчерпывающую, совместимую
библиотеку функ-ций для выполнения операций ввода-вывода, обработки строк ираспределения
памяти, но так как обращение к ним осуществля-ется только явно, можно
, если необходимо, избежать их вызо-ва; эти функции могут быть компактно написаны
на самом “C”. * 8 - Опять же из-за того , что язык “C” отражает возможностисовременных
компьютеров, программы на “C” оказываются доста-точно эффективными,
так что не возникает побуждения писатьвместо этого программы на языке ассемблера.
Наиболее убеди-тельным примером этого является сама операционная система“UNIX”,
которая почти полностью написана на “C”. Из 13000строк программы системы
только около 800 строк самого низко-го уровня написаны на ассемблере. Кроме того,
по существувсе прикладное программное обеспечение системы “UNIX” напи-сано
на “C”; подавляющее большинство пользователей системы“UNIX”(включая одного из
авторов этой книги) даже не знаетязыка ассемблера PDP-11.Хотя “C” соответствует
возможностям многих ЭВМ, он независит от какой-либо конкретной архитектуры машины
и в силуэтого без особых усилий позволяет писать “переносимые” прог-раммы,
т.е. программы, которые можно пропускать без измене-ний на различных аппаратных
средствах. В наших кругах сталуже традицией перенос программного обеспечения,
разработан-ного на системе “UNIX”, на системы ЭВМ: HONEYWELL, IBM иINTERDATA. Фактически
компиляторы с “C” и программное обес-печение во время прогона программ
на этих четырех системах,по-видимому, гораздо более совместимы, чем стандартные
вер-сии фортрана американского национального института стандар-тов (ANSI). Сама
операционная система “UNIX” теперь работаеткак на PDP-11, так и на INTERDATA
8/32. За исключением прог-рамм, которые неизбежно оказываются в некоторой степени
ма-шинно-зависимыми, таких как компилятор, ассемблер и отлад-чик. Написанное
на языке “C” программное обеспечение иден-тично на обеих машинах. Внутри самой
операционной системы7000 строк программы, исключая математическое обеспечениеязыка
ассемблера ЭВМ и управления операциями ввода-вывода,совпадают на 95 процентов.Программистам,
знакомым с другими языками, для сравне-ния и противопоставления
может оказаться полезным упоминаниенескольких исторических, технических и
философских аспектов“C”.Многие из наиболее важных идей “C” происходят от гораз-до
более старого, но все еще вполне жизненного языка BCPL ,разработанного Мартином
Ричардсом. Косвенно язык BCPL оказалвлияние на “C” через язык “B”, написанный
Кеном Томпсоном в1970 году для первой операционной системы “UNIX” на ЭВМPDP-7.Хотя
язык “C” имеет несколько общих с BCPL характерныхособенностей, он никоим
образом не является диалектом пос-леднего. И BCPL и “B” - “безтипные” языки;
единственным ви-дом данных для них являются машинное слово, а доступ к дру-гим
объектам реализуется специальными операторами или обра-щением к функциям. В языке
“C” объектами основных типов дан-ных являются символы, целые числа нескольких
размеров и чис-ла с плавающей точкой. Кроме того, имеется иерархия произ-водных
типов данных, создаваемых указателями, массивами,структурами, объединениями
и функциями. * 9 - Язык “C” включает основные конструкции потока управле-ния, требуемые
для хорошо структуированных программ: группи-рование операторов, принятие
решений (IF), циклы с проверкойзавершения в начале (WHILE, FOR) или в конце
(DO) и выбородного из множества возможных вариантов (SWITCH). (Все этивозможности
обеспечивались и в BCPL, хотя и при несколькоотличном синтаксисе; этот язык
предчувствовал наступившуючерез несколько лет моду на структурное программирование).В
языке “C” имеются указатели и возможность адреснойарифметики. Аргументы
передаются функциям посредством копи-рования значения аргумента , и вызванная
функция не можетизменить фактический аргумент в вызывающей программе. Еслижелательно
добиться “вызова по ссылке”, можно неявно пере-дать указатель, и функция
сможет изменить объект, на которыйэтот указатель указывает. Имена массивов передаются
указани-ем начала массивов, так что аргументы типа массивов эффек-тивно
вызываются по ссылке.К любой функции можно обращаться рекурсивно, и ее ло-кальные
переменные обычно “автоматические”, т.е. Создаютсязаново при каждом обращении.
Описание одной функции не можетсодержаться внутри другой, но переменные могут
описываться всоответствии с обычной блочной структурой. Функции в “C” -программе
могут транслироваться отдельно. переменные по от-ношению к функции могут быть
внутренними, внешними, но из-вестными только в пределах одного исходного файла,
или пол-ностью глобальными. Внутренние переменные могут быть автома-тическими
или статическими. Автоматические переменные длябольшей эффективности можно помещать
в регистры, но объявле-ние регистра является только указанием для компилятора
и ни-как не связано с конкретными машинными регистрами.Язык “C” не является
языком со строгими типами в смыслепаскаля или алгола 68. Он сравнительно снисходителен
к пре-образованию данных, хотя и не будет автоматически преобразо-вывать
типы данных с буйной непринужденностью языка PL/1.Существующие компиляторы
не предусматривают никакой проверкиво время выполнения программы индексов массивов,
типов аргу-ментов и т.д.В тех ситуациях, когда желательна строгая проверка
ти-пов, используется специальная версия компилятора. Эта прог-рамма называется
LINT очевидно потому, она выбирает кусочкипуха из вашей программы. Программа
LINT не генерирует машин-ного кода, а делает очень строгую проверку всех тех сторонпрограммы,
которые можно проконтролировать во время компиля-ции и загрузки.
Она определяет несоответствие типов, несов-местимость аргументов, неиспользованные
или очевидным обра-зом неинициализированные переменные, потенциальные трудностипереносимости
и т.д. Для программ,которые благополучно про-ходят через LINT,
гарантируется отсутствие ошибок типа при-мерно с той же полнотой, как и для
программ, написанных,например, на АЛГОЛЕ-68. Другие возможности программы LINTбудут
отмечены, когда представится соответствующий случай. * 10 - Наконец, язык
“C”, подобно любому другому языку, имеетсвои недостатки. Некоторые операции имеют
неудачное старшин-ство; некоторые разделы синтаксиса могли бы быть лучше; су-шествует
несколько версий языка, отличающихся небольшими де-талями. Тем не менее
язык “C” зарекомендовал себя как исклю-чительно эффективный и выразительный
язык для широкого раз-нообразия применений программирования.Содержание книги организовано
следующим образом. Глава1 является учебным введением в центральную
часть языка “C”.Цель - позволить читателю стартовать так быстро,как тольковозможно,
так как мы твердо убеждены, что единственный спо-соб изучить новый язык -
писать на нем программы. При этом ,однако, предполагается рабочее владение основными
элементамипрограммирования; здесь не объясняется, что такое ЭВМ иликомпилятор,
не поясняется смысл выражений типа N=N+1. Хотямы и пытались, где это возможно,
продемонстрировать полезнуютехнику программирования. Эта книга не предназначается
бытьсправочным руководством по структурам данных и алгоритмам;там, где
мы вынуждены были сделать выбор, мы концентрирова-лись на языке.В главах со 2-й
по 6-ю различные аспекты “C” излагаютсяболее детально и несколько более формально,
чем в главе 1,хотя ударение по-прежнему делается на разборе примеров за-конченных,
полезных программ, а не на отдельных фрагментах.В главе 2 обсуждаются
основные типы данных, операторы ивыражения. В главе 3 рассматриваются управляющие
операторы:IF-ELSE ,WHILE ,FOR и т.д. Глава 4 охватывает функции иструктуру
программы - внешние переменные, правила определен-ных областей действия описания
и т.д. В главе 5 обсуждаютсяуказатели и адресная арифметика. Глава 6 содержит
подробноеописание структур и объединений.В главе 7 описывается стандартная библиотека
ввода-вы-вода языка “C”, которая обеспечивает стандартный интерфейс соперационной
системой. Эта библиотека ввода-вывода поддержи-вается на всех машинах,
на которых реализован “C”, так чтопрограммы, использующие ее для ввода, вывода
и других сис-темных функций, могут переноситься с одной системы на другуюпо
существу без изменений.В главе 8 описывается интерфейс между “C” - программамии
операционной системой “UNIX”. Упор делается на ввод-вывод,систему файлов и переносимость.
Хотя некоторые части этойглавы специфичны для операционной системы
“UNIX”, програм-мисты, не использующие “UNIX”, все же должны найти здесь по-лезный
материал, в том числе некоторое представление о том,как реализована одна версия
стандартной библиотеки и предло-жения для достижения переносимости программы.Приложение
A содержит справочное руководство по языку“C”. Оно является “официальным”
изложением синтаксиса и се-мантики “C” и (исключая чей-либо собственный
компилятор)окончательным арбитром для всех двусмысленностей и упущенийв предыдущих
главах. * 11 - Так как “C” является развивающимся языком, реализован-ным
на множестве систем, часть материла настоящей книги мо-жет не соответствовать текущему
состоянию разработки на ка-кой-то конкретной системе. Мы старались избегать
таких проб-лем и предостерегать о возможных трудностях. В сомнительныхслучаях,
однако, мы обычно предпочитали описывать ситуациюдля системы “UNIX” PDP-11
, так как она является средой длябольшинства программирующих на языке “C”. В приложении
атакже описаны расхождения в реализациях языка “C” на основ-ных системах.1. Учебное
введение.Давайте начнем с быстрого введения в язык “C”. Нашацель
- продемонстрировать существенные элементы языка на ре-альных программах, не увязая
при этом в деталях, формальныхправилах и исключениях. В этой главе мы не
пытаемся изложитьязык полностью или хотя бы строго (разумеется, приводимыепримеры
будут корректными). Мы хотим как можно скорее довес-ти вас до такого уровня,
на котором вы были бы в состоянииписать полезные программы, и чтобы добиться этого,
мы сосре-дотачиваемся на основном: переменных и константах, арифмети-ке,
операторах передачи управления, функциях и элементарныхсведениях о вводе и выводе.
Мы совершенно намеренно оставля-ем за пределами этой главы многие элементы
языка “C”, кото-рые имеют первостепенное значение при написании большихпрограмм,
в том числе указатели, сртуктуры, большую часть избогатого набора операторов
языка “C”, несколько операторовпередачи управления и несметное количество деталей.Такой
подход имеет, конечно, свои недостатки. Самым су-щественным является то,
что полное описание любого конкрет-ного элемента языка не излагается в одном
месте, а поясне-ния, в силу краткости, могут привести к неправильному истол-кованию.
Кроме того, из-за невозможности использовать всюмощь языка, примеры оказываются
не столь краткими и элегант-ными, как они могли бы быть. И хотя мы старались
свести этинедостатки к минимуму, все же имейте их ввиду.Другой недостаток
состоит в том, что последующие главыбудут неизбежно повторять некоторые части
этой главы. Мы на-деемся, что такое повторение будет скорее помогать, чем раз-дражать.Во
всяком случае, опытные программисты должны оказатьсяв состоянии проэкстраполировать
материал данной главы насвои собственные программистские нужды.
Начинающие же должныв дополнение писать аналогичные маленькие самостоятельныепрограммы.
И те, и другие могут использовать эту главу каккаркас, на который будут
навешиваться более подробные описа-ния, начинающиеся с главы 2.1.1. Hачинаем.
Единственный способ освоить новый языкпрограммирования - писать на нем программы.
Первая програм-ма, которая должна быть написана, - одна для всех языков:напечатать
слова : HELLO, WORLD.Это - самый существенный барьер; чтобы преодолеть
его,вы должны суметь завести где-то текст программы, успешно егоскомпилировать,
загрузить, прогнать и найти, где оказаласьваша выдача. Если вы научились справляться
с этими техничес-кими деталями, все остальное сравнительно просто. * 12
- Программа печати “HELLO, WORLD” на языке “C” имеет вид:MAIN () {PRINTF(“HELLO,
WORLDN”); }Как пропустить эту программу - зависит от используемойвами системы.
В частности, на операционной системе “UNIX” выдолжны завести исходную программу
в файле, имя которогооканчивается на “.C” , например, HELLO.C , и затем скомпили-ровать
ее по команде CC HELLO.CЕсли вы не допустили какой-либо небрежности
, такой какпропуск символа или неправильное написание, компиляция прой-дет без
сообщений и будет создан исполняемый файл с именема.OUT . Прогон его по команде
A.OUTприведет к выводуHELLO, WORLDНа других системах эти правила будут иными;
проконсуль-тируйтесь с местным авторитетом.Упражнение 1-1.Пропустите эту программу
на вашей системе. Попробуйтене включать различные части программы и посмотрите
какие со-общения об ошибках вы при этом получите.Теперь некоторые пояснения
к самой программе. Любая“C”-программа, каков бы ни был ее размер, состоит из
однойили более “функций”, указывающих фактические операциикомпьютера, которые
должны быть выполнены. Функции в языке“C” подобны функциям и подпрограммам фортрана
и процедурамPL/1, паскаля и т.д. В нашем примере такой функцией являетсяMAIN.
Обычно вы можете давать функциям любые имена по вашемуусмотрению, но MAIN -
это особое имя; выполнение вашей прог-раммы начинается сначала с функции MAIN.
Это означает, чтокаждая программа должна в каком-то месте содержать функцию сименем
MAIN. Для выполнения определенных действий функцияMAIN обычно обращается
к другим функциям, часть из которыхнаходится в той же самой программе, а часть
- в библиотеках,содержащих ранее написанные функции. * 13 - Одним способом обмена
данными между функциями являетсяпередача посредством аргументов. Круглые скобки,
следующиеза именем функции, заключают в себе список аргументов; здесьмаIN
- функция без аргументов, что указывается как (). Опе-раторы, составляющие функцию,
заключаются в фигурные скобки{ и }, которые аналогичны DO-END в PL/1 или BEGIN-END
в ал-голе, паскале и т.д. Обращение к функции осуществляется ука-занием
ее имени, за которым следует заключенный в круглыескобки список аргументов. здесь
нет никаких операторов CALL,как в фортране или PL/1. Круглые скобки должны
присутство-вать и в том случае, когда функция не имеет аргументов.Строка PRINTF(“HELLO,
WORLDN”);является обращением к функции, которое вызывает функциюс именем
PRINTF и аргуметом “HELLO, WORLDN”. Функция PRINTFявляется библиотечной функцией,
которая выдает выходные дан-ные на терминал (если только не указано какое-то
другое мес-то назначения). В данном случае печатается строка символов,являющаяся
аргументом функции.Последовательность из любого количества символов, зак-люченных
в удвоенные кавычки “...”, называется 'символьнойстрокой' или 'строчной
константой'. Пока мы будем использо-вать символьные строки только в качестве
аргументов дляPRINTF и других функций.Последовательность N в приведенной строке
являетсяобозначением на языке “C” для 'символа новой строки', кото-рый служит
указанием для перехода на терминале к левому краюследующей строки. Если вы
не включите N (полезный экспери-мент), то обнаружите, что ваша выдача не закончится
перехо-дом терминала на новую строку. Использование последователь-ности N
- единственный способ введения символа новой строкив аргумент функции PRINTF;
если вы попробуете что-нибудьвроде PRINTF(“HELLO, WORLD “); то “C”-компилятор
будет печатать злорадные диагностическиесообщения о недостающих кавычках.Функция
PRINTF не обеспечивает автоматического переходана новую строку, так что многократное
обращение к ней можноиспользовать для поэтапной сборки выходной строки.
Наша пер-вая программа, печатающая идентичную выдачу, с точно такимже успехом
могла бы быть написана в виде MAIN() {PRINTF(“HELLO, “);PRINTF(“WORLD”);PRINTF(“N”);
} * 14 - Подчеркнем, что N представляет только один символ. Ус-ловные 'последовательности',
подобные N , дают общий и до-пускающий расширение механизм
для представления трудных дляпечати или невидимых символов. Среди прочих символов
в языке“C” предусмотрены следующие: т - для табуляции, B - длявозврата на
одну позицию, ” - для двойной кавычки и длясамой обратной косой черты.Упражнение
1-2.Проведите эксперименты для того, чтобы узнать что прои-зойдет, если
в строке, являющейся аргументом функции PRINTFбудет содержаться X, где X - некоторый
символ, не входящийв вышеприведенный список.1.2. Переменные и арифметика.Следующая
программа печатает приведенную ниже таблицутемператур по Фаренгейту
и их эквивалентов по стоградуснойшкале Цельсия, используя для перевода формулу
C = (5/9)*(F-32).0 -17.8 20 -6.7 40 4.4 60 15.6 ... ... 260 126.7 280 137.8 300
140.9 Теперь сама программа:/* PRINT FAHRENHEIT-CELSIUS TABLEFOR F = 0, 20, ...,
300 */MAIN() {INT LOWER, UPPER, STEP;FLOAT FAHR, CELSIUS;LOWER = 0; /* LOWER
LIMIT OF TEMPERATURETABLE */UPPER =300; /* UPPER LIMIT */STEP = 20; /* STEP SIZE
*/FAHR = LOWER;WHILE (FAHR = '0' &C = '0' &C = '0' &C 0 */INT X,N; {INT I,
P;P = 1;FOR (I =1; I 0;VERSION 2 */INT X,N; {INT P;FOR (P = 1; N > 0; --N)P = P
* X;RETURN (P); } Аргумент N используется как временная переменная; из не-го вычитается
единица до тех пор, пока он не станет нулем.Переменная I здесь больше
не нужна. чтобы ни происходило с Nвнутри POWER это никак не влияет на аргумент,
с которым пер-воначально обратились к функции POWER.При необходимости все же
можно добиться, чтобы функцияизменила переменную из вызывающей программы. Эта программадолжна
обеспечить установление адреса переменной /техничес-ки, через указатель
на переменную/, а в вызываемой функциинадо описать соответствующий аргумент
как указатель и ссы-латься к фактической переменной косвенно через него. Мы
рас-смотрим это подробно в главе 5.Когда в качестве аргумента выступает имя массива,
тофактическим значением, передаваемым функции, является адресначала массива.
/Здесь нет никакого копирования элементовмассива/. С помощью индексации и
адреса начала функция можетнайти и изменить любой элемент массива. Это - тема
следующе-го раздела. * 32 - 1.9. Массивы символов. По-видимому самым общим типом
массива в “C” являетсямассив символов. Чтобы проиллюстрировать использование
мас-сивов символов и обрабатывающих их функций, давайте напишемпрограмму, которая
читает набор строк и печатает самую длин-ную из них. Основная схема программы
достаточно проста: WHILE (имеется еще строка)IF (эта строка длиннее самой длинной
изпредыдущих)запомнить эту строку и ее длинунапечатать самую длинную строкуПо
этой схеме ясно, что программа естественным образомраспадается на несколько
частей. Одна часть читает новуюстроку, другая проверяет ее, третья запоминает,
а остальныечасти программы управляют этим процессом.Поскольку все так прекрасно
делится, было бы хорошо инаписать программу соответсвующим образом. Давайте сначаланапишем
отдельную функцию GETLINE, которая будет извлекатьследующую строку
из файла ввода; это - обобщение функцииGETCHAR. мы попытаемся сделать эту функцию
по возможностиболее гибкой, чтобы она была полезной и в других ситуациях.Как
минимум GETLINE должна передавать сигнал о возможном по-явлении конца файла;
более общий полезный вариант мог бы пе-редавать длину строки или нуль, если встретится
конец файла.нуль не может быть длиной строки, так как каждая строка со-держит
по крайней мере один символ; даже строка, содержащаятолько символ новой
строки, имеет длину 1.Когда мы находим строку, которая длиннее самой длиннойиз
предыдущих, то ее надо где-то запомнить. Это наводит намысль о другой функции,
COPY , которая будет копировать но-вую строку в место хранения.Наконец, нам нужна
основная программа для управленияфункциями GETLINE и COPY . Вот результат :#DEFINE
MAXLINE 1000 /* MAXIMUM INPUTLINE SIZE */MAIN() /* FIND LONGEST LINE */
{INT LEN; /* CURRENT LINE LENGTH */INT MAX; /* MAXIMUM LENGTH SEEN SO FAR */CHAR
LINE[MAXLINE]; /* CURRENT INPUT LINE */CHAR SAVE[MAXLINE]; /* LONGEST LINE,
SAVED */ MAX = 0;WHILE ((LEN = GETLINE(LINE, MAXLINE)) > 0)IF (LEN > MAX) {MAX
= LEN;COPY(LINE, SAVE); }IF (MAX > 0) /* THERE WAS A LINE */PRINTF(“%S”, SAVE);
} * 33 - GETLINE(S,LIM) /* GET LINE INTO S,RETURN LENGTH */CHAR S[];INT LIM;
{INT C, I;FOR(I=0;I 0 )IF ( LEN > MAX ) {MAX = LEN;COPY(); }IF ( MAX > 0 ) /* THERE
WAS A LINE */PRINTF( “%S”, SAVE ); } GETLINE() /* SPECIALIZED VERSION */ {INT
C, I;EXTERN CHAR LINE[];FOR (I = 0; I > =='0' &S[I]= 'A' &C J, илогические выражения,
связанные операциями &и !!, по оп-ределению имеют значение 1, если
они истинны, и 0, если ониложны. Таким образом, присваивание ISDIGIT = C >= '0'
&C > сдвиг вправо ^ дополнение (унарная операция) “” иммитирует вертикальную
черту.Побитовая операция AND часто используется для маскированиянекоторого множества
битов; например, оператор C = N 0177 * 52 - передает в 'с' семь младших
битов N , полагая остальные рав-ными нулю. Операция 'э' побитового OR используется
для вклю-чения битов: C = X э MASKустанавливает на единицу те биты в х , которые
равны единицев MASK.Следует быть внимательным и отличать побитовые операциии
'э' от логических связок &и !! , Которые подразуме-вают вычисление значения
истинности слева направо. Например,если х=1, а Y=2, то значение хYравно нулю
, в то время какзначение X&Yравно единице./почему?/Операции сдвига > осуществляют
соответственносдвиг влево и вправо своего левого операнда на число битовыхпозиций,
задаваемых правым операндом. Таким образом , х> (P+1-N)) ^(^0 > (P+1-N)
сдвигает желаемое поле в правый конецслова. Описание аргумента X как UNSIGNED
гарантирует, чтопри сдвиге вправо освобождающиеся биты будут заполняться ну-лями,
а не содержимым знакового бита, независимо от того, накакой машине пропускается
программа. Все биты константноговыражения ^0 равны 1; сдвиг его на N позиций
влево с по-мощью операции ^0> ^ ! Если е1 и е2 - выражения, то * 54 -
е1 оп= е2эквивалентное1 = (е1) оп (е2)за исключением того, что выражение е1 вычисляется
толькоодин раз. Обратите внимание на круглые скобки вокруг е2: X *= Y +
1тоX = X * (Y + 1)неX = X * Y + 1В качестве примера приведем функцию BITCOUNT,
которая подсчитывает число равных 1 битов у целого аргумента. BITCOUNT(N) /* COUNT
1 BITS IN N */UNSIGNED N; (INT B;FOR (B = 0; N != 0; N >>= 1)IF (N 01)B++;RETURN(B);
) Не говоря уже о краткости, такие операторы приваиванияимеют то преимущество,
что они лучше соответствуют образучеловеческого мышления. Мы говорим:
“прибавить 2 к I” или“увеличить I на 2”, но не “взять I, прибавить 2 и поместитьрезультат
опять в I”. Итак, I += 2. Кроме того, в громоздкихвыражениях, подобных
YYVAL[YYPV[P3+P4] + YYPV[P1+P2]] += 2Tакая операция присваивания облегчает
понимание программы,так как читатель не должен скрупулезно проверять, являютсяли
два длинных выражения действительно одинаковыми, или за-думываться, почему они
не совпадают. Такая операция присваи-вания может даже помочь компилятору получить
более эффектив-ную программу.Мы уже использовали тот факт, что операция присваиванияимеет
некоторое значение и может входить в выражения; самыйтипичный
пример * 55 - WHILE ((C = GETCHAR()) != EOF)присваивания, использующие другие операции
присваивания (+=,-= и т.д.) также могут входить в выражения, хотя это случа-ется
реже.Типом выражения присваивания является тип его левогооперанда.Упражнение
2-9.В двоичной системе счисления операция X(X-1)обнуляетсамый правый равный
1 бит переменной X.(почему?) используйтеэто замечание для написания более
быстрой версии функцииBITCOUNT. 2.11. Условные выражения. ОператорыIF (A > B)Z
= A;ELSEZ = B;конечно вычисляют в Z максимум из а и в. Условное выражение,записанное
с помощью тернарной операции “?:”, предоставляетдругую возможность для записи
этой и аналогичных конструк-ций. В выражении е1 ? Е2 : е3сначала вычисляется
выражение е1. Если оно отлично от нуля(истинно), то вычисляется выражение е2,
которое и становитсязначением условного выражения. В противном случае вычисляет-ся
е3, и оно становится значением условного выражения. Каж-дый раз вычисляется
только одно из выражения е2 и е3. Такимобразом, чтобы положить Z равным максимуму
из а и в, можнонаписать Z = (A > B) ? A : B; /* Z = MAX(A,B) */Следует подчеркнуть,
что условное выражение действитель-но является выражением и может использоваться
точно так же,как любое другое выражение. Если е2 и е3 имеют разные
типы,то тип результата определяется по правилам преобразования,рассмотренным ранее
в этой главе. например, если F имеет типFLOAT, а N - тип INT, то выражение
(N > 0) ? F : NИмеет тип DOUBLE независимо от того, положительно ли N илинет.
* 56 - Так как уровень старшинства операции ?: очень низок,прямо над присваиванием,
то первое выражение в условном вы-ражении можно не заключать в круглые скобки.
Однако, мы всеже рекомендуем это делать, так как скобки делают условнуючасть
выражения более заметной.Использование условных выражений часто приводит к корот-ким
программам. Например, следующий ниже оператор цикла пе-чатает N элементов
массива, по 10 в строке, разделяя каждыйстолбец одним пробелом и заканчивая
каждую строку (включаяпоследнюю) одним символом перевода строки. OR (I = 0; I
. LEFT TO RIGHT ! ^ ++ -- - (TYPE)