В главе 2 обсуждаются основные типы данных, операторы и выражения. В главе 3
рассматриваются управляющие операторы: IF-ELSE ,WHILE ,FOR и т.д. Глава 4 охватывает
функции и структуру программы - внешние переменные, правила определен- ных областей
действия описания и т.д. В главе 5 обсуждаются указатели и адресная арифметика.
Глава 6 содержит подробное описание структур и объединений.
В главе 7 описывается стандартная библиотека ввода-вы- вода языка "C", которая
обеспечивает стандартный интерфейс с операционной системой. Эта библиотека ввода-вывода
поддержи- вается на всех машинах, на которых реализован "C", так что программы,
использующие ее для ввода, вывода и других сис- темных функций, могут переноситься
с одной системы на другую по существу без изменений.
В главе 8 описывается интерфейс между "C" - программами и операционной системой
"UNIX". Упор делается на ввод-вывод, систему файлов и переносимость. Хотя некоторые
части этой главы специфичны для операционной системы "UNIX", програм- мисты, не
использующие "UNIX", все же должны найти здесь по- лезный материал, в том числе
некоторое представление о том, как реализована одна версия стандартной библиотеки
и предло- жения для достижения переносимости программы.
Приложение A содержит справочное руководство по языку "C". Оно является "официальным"
изложением синтаксиса и се- мантики "C" и (исключая чей-либо собственный компилятор)
окончательным арбитром для всех двусмысленностей и упущений в предыдущих главах.
Так как "C" является развивающимся языком, реализован- ным на множестве систем,
часть материла настоящей книги мо- жет не соответствовать текущему состоянию разработки
на ка- кой-то конкретной системе. Мы старались избегать таких проб- лем и предостерегать
о возможных трудностях. В сомнительных случаях, однако, мы обычно предпочитали описывать
ситуацию для системы "UNIX" PDP-11 , так как она является средой для большинства
программирующих на языке "C". В приложении а также описаны расхождения в реализациях
языка "C" на основ- ных системах.
* 1. Учебное введение *
Давайте начнем с быстрого введения в язык "C". Наша цель - продемонстрировать
существенные элементы языка на ре- альных программах, не увязая при этом в деталях,
формальных правилах и исключениях. В этой главе мы не пытаемся изложить язык полностью
или хотя бы строго (разумеется, приводимые примеры будут корректными). Мы хотим
как можно скорее довес- ти вас до такого уровня, на котором вы были бы в состоянии
писать полезные программы, и чтобы добиться этого, мы сосре- дотачиваемся на основном:
переменных и константах, арифмети- ке, операторах передачи управления, функциях
и элементарных сведениях о вводе и выводе. Мы совершенно намеренно оставля- ем за
пределами этой главы многие элементы языка "C", кото- рые имеют первостепенное значение
при написании больших программ, в том числе указатели, сртуктуры, большую часть
из богатого набора операторов языка "C", несколько операторов передачи управления
и несметное количество деталей.
Такой подход имеет, конечно, свои недостатки. Самым су- щественным является то,
что полное описание любого конкрет- ного элемента языка не излагается в одном месте,
а поясне- ния, в силу краткости, могут привести к неправильному истол- кованию.
Кроме того, из-за невозможности использовать всю мощь языка, примеры оказываются
не столь краткими и элегант- ными, как они могли бы быть. И хотя мы старались свести
эти недостатки к минимуму, все же имейте их ввиду.
Другой недостаток состоит в том, что последующие главы будут неизбежно повторять
некоторые части этой главы. Мы на- деемся, что такое повторение будет скорее помогать,
чем раз- дражать.
Во всяком случае, опытные программисты должны оказаться в состоянии проэкстраполировать
материал данной главы на свои собственные программистские нужды. Начинающие же должны
в дополнение писать аналогичные маленькие самостоятельные программы. И те, и другие
могут использовать эту главу как каркас, на который будут навешиваться более подробные
описа- ния, начинающиеся с главы 2.
1.1. Hачинаем
Единственный способ освоить новый язык программирования - писать на нем программы.
Первая програм- ма, которая должна быть написана, - одна для всех языков: напечатать
слова : HELLO, WORLD.
Это - самый существенный барьер; чтобы преодолеть его, вы должны суметь завести
где-то текст программы, успешно его скомпилировать, загрузить, прогнать и найти,
где оказалась ваша выдача. Если вы научились справляться с этими техничес- кими
деталями, все остальное сравнительно просто.
Программа печати "HELLO, WORLD" на языке "C" имеет вид:
MAIN ()
{
PRINTF("HELLO, WORLD\N");
}
Как пропустить эту программу - зависит от используемой вами системы. В частности,
на операционной системе "UNIX" вы должны завести исходную программу в файле, имя
которого оканчивается на ".C" , например, HELLO.C , и затем скомпили- ровать ее
по команде
CC HELLO.C
Если вы не допустили какой-либо небрежности , такой как пропуск символа или неправильное
написание, компиляция прой- дет без сообщений и будет создан исполняемый файл с
именем а.OUT . Прогон его по команде
A.OUT
приведет к выводу
HELLO, WORLD
На других системах эти правила будут иными; проконсуль- тируйтесь с местным авторитетом.
Упражнение 1-1
Пропустите эту программу на вашей системе. Попробуйте не включать различные части
программы и посмотрите какие со- общения об ошибках вы при этом получите.
Теперь некоторые пояснения к самой программе. Любая "C"-программа, каков бы ни
был ее размер, состоит из одной или более "функций", указывающих фактические операции
компьютера, которые должны быть выполнены. Функции в языке "C" подобны функциям
и подпрограммам фортрана и процедурам PL/1, паскаля и т.д. В нашем примере такой
функцией является MAIN. Обычно вы можете давать функциям любые имена по вашему усмотрению,
но MAIN - это особое имя; выполнение вашей прог- раммы начинается сначала с функции
MAIN. Это означает, что каждая программа должна в каком-то месте содержать функцию
с именем MAIN. Для выполнения определенных действий функция MAIN обычно обращается
к другим функциям, часть из которых находится в той же самой программе, а часть
- в библиотеках, содержащих ранее написанные функции.
Одним способом обмена данными между функциями является передача посредством аргументов.
Круглые скобки, следующие за именем функции, заключают в себе список аргументов;
здесь маIN - функция без аргументов, что указывается как (). Опе- раторы, составляющие
функцию, заключаются в фигурные скобки { и }, которые аналогичны DO-END в PL/1 или
BEGIN-END в ал- голе, паскале и т.д. Обращение к функции осуществляется ука- занием
ее имени, за которым следует заключенный в круглые скобки список аргументов. здесь
нет никаких операторов CALL, как в фортране или PL/1. Круглые скобки должны присутство-
вать и в том случае, когда функция не имеет аргументов. Строка
PRINTF("HELLO, WORLD\N");
является обращением к функции, которое вызывает функцию с именем PRINTF и аргуметом
"HELLO, WORLD\N". Функция PRINTF является библиотечной функцией, которая выдает
выходные дан- ные на терминал (если только не указано какое-то другое мес- то назначения).
В данном случае печатается строка символов, являющаяся аргументом функции.
Последовательность из любого количества символов, зак- люченных в удвоенные кавычки
"...", называется 'символьной строкой' или 'строчной константой'. Пока мы будем
использо- вать символьные строки только в качестве аргументов для PRINTF и других
функций.
Последовательность \N в приведенной строке является обозначением на языке "C"
для 'символа новой строки', кото- рый служит указанием для перехода на терминале
к левому краю следующей строки. Если вы не включите \N (полезный экспери- мент),
то обнаружите, что ваша выдача не закончится перехо- дом терминала на новую строку.
Использование последователь- ности \N - единственный способ введения символа новой
строки в аргумент функции PRINTF; если вы попробуете что-нибудь вроде
PRINTF("HELLO, WORLD
");
то "C"-компилятор будет печатать злорадные диагностические сообщения о недостающих
кавычках.
Функция PRINTF не обеспечивает автоматического перехода на новую строку, так
что многократное обращение к ней можно использовать для поэтапной сборки выходной
строки. Наша пер- вая программа, печатающая идентичную выдачу, с точно таким же
успехом могла бы быть написана в виде
MAIN()
{
PRINTF("HELLO, ");
PRINTF("WORLD");
PRINTF("\N");
}
Подчеркнем, что \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 TABLE
FOR F = 0, 20, ..., 300 */
MAIN()
{
INT LOWER, UPPER, STEP;
FLOAT FAHR, CELSIUS;
LOWER = 0; /* LOWER LIMIT OF TEMPERATURE
TABLE */
UPPER =300; /* UPPER LIMIT */
STEP = 20; /* STEP SIZE */
FAHR = LOWER;
WHILE (FAHR <= UPPER) {
CELSIUS = (5.0/9.0) * (FAHR -32.0);
PRINTF("%4.0F %6.1F\N", FAHR, CELSIUS);
FAHR = FAHR + STEP;
}
}
Первые две строки
/* PRINT FAHRENHEIT-CELSIUS TABLE
FOR F = 0, 20, ..., 300 */
являются комментарием, который в данном случае кратко пояс- няет, что делает
программа. Любые символы между /* и */ иг- норируются компилятором; можно свободно
пользоваться коммен- тариями для облегчения понимания программы. Комментарии мо-
гут появляться в любом месте, где возможен пробел или пере- ход на новую строку.
В языке "C" все переменные должны быть описаны до их ис- пользования, обычно
это делается в начале функции до первого выполняемого оператора. Если вы забудете
вставить описание, то получите диагностическое сообщение от компилятора. Описа-
ние состоит из типа и списка переменных, имеющих этот тип, как в
INT LOWER, UPPER, STEP;
FLOAT FAHR, CELSIUS;
Тип INT означает, что все переменные списка целые; тип FLOAT предназначен для
чисел с плавающей точкой, т.е. для чисел, которые могут иметь дробную часть. Точность
как INT , TAK и FLOAT зависит от конкретной машины, на которой вы ра- ботаете. На
PDP-11, например, тип INT соответствует 16-бито- вому числу со знаком, т.е. числу,
лежащему между -32768 и +32767. Число типа FLOAT - это 32-битовое число, имеющее
около семи значащих цифр и лежащее в диапазоне от 10е-38 до 10е+38. В главе 2 приводится
список размеров для других ма- шин.
В языке "C" предусмотрено несколько других основных ти- пов данных, кроме INT
и FLOAT: CHAR символ - один байт SHORT короткое целое LONG длинное целое DOUBLE
плавающее с двойной точностью
Размеры этих объектов тоже машинно-независимы; детали приведены в главе 2. Имеются
также массивы, структуры и об- ъединения этих основных типов, указатели на них и
функ- ции,которые их возвращают; со всеми ними мы встретимся в свое время.
Фактически вычисления в программе перевода температур начинаются с операторов
присваивания LOWER = 0; UPPER =300; STEP = 20; FAHR =LOWER; которые придают переменным
их начальные значения. каждый от- дельный оператор заканчивается точкой с запятой.
Каждая строка таблицы вычисляется одинаковым образом, так что мы используем цикл,
повторяющийся один раз на стро- ку. В этом назначение оператора WHILE:
WHILE (FAHR <= UPPER) { .... }
проверяется условие в круглых скобках. Если оно истинно (FAHR меньше или равно
UPPER), то выполняется тело цикла (все операторы, заключенные в фигурные скобки
{ и } ). Затем вновь проверяется это условие и, если оно истинно, опять вы- полняется
тело цикла. Если же условие не выполняется ( FAHR превосходит UPPER ), цикл заканчивается
и происходит переход к выполнению оператора, следующего за оператором цикла. Так
как в настоящей программе нет никаких последующих операто- ров, то выполнение программы
завершается.
Тело оператора WHILE может состоять из одного или более операторов, заключенных
в фигурные скобки, как в программе перевода температур, или из одного оператора
без скобок, как, например, в
WHILE (I < J)
I = 2 * I;
В обоих случаях операторы, управляемые оператором WHILE, сдвинуты на одну табуляцию,
чтобы вы могли с первого взгляда видеть, какие операторы находятся внутри цикла.
Такой сдвиг подчеркивает логическую структуру программы. Хотя в языке "C" допускается
совершенно произвольное расположение опера- торов в строке, подходящий сдвиг и использование
пробелов значительно облегчают чтение программ. Мы рекомендуем писать только один
оператор на строке и (обычно) оставлять пробелы вокруг операторов. Расположение
фигурных скобок менее сущес- твенно; мы выбрали один из нескольких популярных стилей.
Вы- берите подходящий для вас стиль и затем используйте его пос- ледовательно.
Основная часть работы выполняется в теле цикла. Темпера- тура по Цельсию вычисляется
и присваивается переменной CELAIUS оператором
CELSIUS = (5.0/9.0) * (FAHR-32.0);
причина использования выражения 5.0/9.0 вместо выглядящего проще 5/9 заключается
в том, что в языке "C", как и во мно- гих других языках, при делении целых происходит
усечение, состоящее в отбрасывании дробной части результата. Таким об- разом, результат
операции 5/9 равен нулю, и, конечно, в этом случае все температуры оказались бы
равными нулю. Десятичная точка в константе указывает, что она имеет тип с плавающей
точкой, так что, как мы и хотели, 5.0/9.0 равно 0.5555... .