богатого набора операторов языка "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... .
Мы также писали 32.0 вместо 32 , несмотря на то, что так
как переменная FAHR имеет тип FLOAT , целое 32 автоматически
бы преобразовалось к типу FLOAT ( в 32.0) перед вычитанием.
С точки зрения стиля разумно писать плавающие константы с
явной десятичной точкой даже тогда, когда они имеют целые
значения; это подчеркивает их плавающую природу для просмат-
ривающего программу и обеспечивает то, что компилятор будет
смотреть на вещи так же, как и Вы.



Подробные правила о том, в каком случае целые преобразу-
ются к типу с плаваюшей точкой, приведены в главе 2. Сейчас
же отметим, что присваивание

FAHR = LOWER;

проверка

WHILE (FAHR <= UPPER)

работают, как ожидается, - перед выполнением операций целые
преобразуются в плавающую форму.
Этот же пример сообщает чуть больше о том, как работает
PRINTF. Функция PRINTF фактически является универсальной
функцией форматных преобразований, которая будет полностью
описана в главе 7. Ее первым аргументом является строка сим-
волов, которая должна быть напечатана, причем каждый знак %
указывает, куда должен подставляться каждый из остальных ар-
гументов /второй, третий, .../ и в какой форме он должен пе-
чататься. Например, в операторе

PRINTF("%4.0F %6.1F\N", FAHR, CELSIUS);

спецификация преобразования %4.0F говорит, что число с пла-
вающей точкой должно быть напечатано в поле шириной по край-
ней мере в четыре символа без цифр после десятичной точки.
спецификация %6.1F описывает другое число, которое должно
занимать по крайней мере шесть позиций с одной цифрой после
десятичной точки, аналогично спецификациям F6.1 в фортране
или F(6,1) в PL/1. Различные части спецификации могут быть
опущены: спецификация %6F говорит, что число будет шириной
по крайней мере в шесть символов; спецификация %2 требует
двух позиций после десятичной точки, но ширина при этом не
ограничивается; спецификация %F говорит только о том, что
нужно напечатать число с плавающей точкой. Функция PRINTF
также распознает следующие спецификации: %D - для десятично-
го целого, %о - для восьмеричного числа, %х - для шестнадца-
тиричного, %с - для символа, %S - для символьной строки и %%
- для самого символа %.
Каждая конструкция с символом % в первом аргументе функ-
ции PRINTF сочетается с соответствующим вторым, третьим, и
т.д. Аргументами; они должны согласовываться по числу и ти-
пу; в противном случае вы получите бессмысленные результаты.
Между прочим, функция PRINTF не является частью языка
"C"; в самом языке "C" не определены операции ввода-вывода.
Нет ничего таинственного и в функции PRINTF ; это - просто
полезная функция, являющаяся частью стандартной библиотеки
подпрограмм, которая обычно доступна "C"-программам. Чтобы
сосредоточиться на самом языке, мы не будем подробно оста-
навливаться на операциях ввода-вывода до главы 7. В частнос-
ти, мы до тех пор отложим форматный ввод. Если вам надо
ввести числа - прочитайте описание функции SCANF в главе 7,
раздел 7.4. Функция SCANF во многом сходна с PRINTF , но она
считывает входные данные, а не печатает выходные.




Упражнение 1-3
----------------
Преобразуйте программу перевода температур таким обра-
зом, чтобы она печатала заголовок к таблице.

Упражнение 1-4
----------------
Напишите программы печати соответствующей таблицы пере-
хода от градусов цельсия к градусам фаренгейта.


    1.3. Оператор FOR



Как и можно было ожидать, имеется множество различных
способов написания каждой программы. Давайте рассмотрим та-
кой вариант программы перевода температур:

MAIN() /* FAHRENHEIT-CELSIUS TABLE */
{
INT FAHR;
FOR (FAHR = 0; FAHR <= 300; FAHR = FAHR + 20)
PRINTF("%4D %6.1F\N", FAHR, (5.0/9.0)*(FAHR-32.0));
}

Эта программа выдает те же самые результаты, но выглядит
безусловно по-другому. Главное изменение - исключение боль-
шинства переменных; осталась только переменная FAHR , причем
типа INT (это сделано для того, чтобы продемонстрировать
преобразование %D в функции PRINTF). Нижняя и верхняя грани-
цы и размер щага появляются только как константы в операторе
FOR , который сам является новой конструкцией, а выражение,
вычисляющее температуру по цельсию, входит теперь в виде
третьего аргумента функции PRINTF , а не в виде отдельного
оператора присваивания.
Последнее изменение является примером вполне общего пра-
вила языка "C" - в любом контексте, в котором допускается
использование значения переменной некоторого типа, вы можете
использовать выражение этого типа. Так как третий аргумент
функции PRINTF должен иметь значение с плавающей точкой,
чтобы соответствовать спецификации %6.1F, то в этом месте
может встретиться любое выражение плавающего типа.
Сам оператор FOR - это оператор цикла, обобщающий опера-
тор WHILE. Его функционирование должно стать ясным, если вы
сравните его с ранее описанным оператором WHILE . Оператор
FOR содержит три части, разделяемые точкой с запятой. Первая
часть

FAHR = 0

выполняется один раз перед входом в сам цикл. Вторая часть -
проверка, или условие, которое управляет циклом:

FAHR <= 300

это условие проверяется и, если оно истинно, то выполняется
тело цикла (в данном случае только функция PRINTF ). Затем
выполняется шаг реинициализации
FAHR =FAHR + 20

и условие проверяется снова. цикл завершается, когда это ус-
ловие становится ложным. Так же, как и в случае оператора
WHILE , тело цикла может состоять из одного оператора или из
группы операторов, заключенных в фигурные скобки. Инициали-
зирующая и реинициализирующая части могут быть любыми от-
дельными выражениями.
Выбор между операторами WHILE и FOR произволен и основы-
вается на том , что выглядит яснее. Оператор FOR обычно удо-
бен для циклов, в которых инициализация и реинициализация
логически связаны и каждая задается одним оператором, так
как в этом случае запись более компактна, чем при использо-
вании оператора WHILE , а операторы управления циклом сосре-
дотачиваются вместе в одном месте.

Упражнение 1-5
---------------
Модифицируйте программу перевода температур таким обра-
зом, чтобы она печатала таблицу в обратном порядке, т.е. От
300 градусов до 0.

    1.4. Символические константы



Последнее замечание, прежде чем мы навсегда оставим
программу перевода температур. Прятать "магические числа",
такие как 300 и 20, внутрь программы - это неудачная практи-
ка; они дают мало информации тем, кто, возможно, должен бу-
дет разбираться в этой программе позднее, и их трудно изме-
нять систематическим образом. К счастью в языке "C" предус-
мотрен способ, позволяющий избежать таких "магических чи-
сел". Используя конструкцию #DEFINE , вы можете в начале
программы определить символическое имя или символическую
константу, которая будет конкретной строкой символов. Впос-
ледствии компилятор заменит все не заключенные в кавычки по-
явления этого имени на соответствующую строку. Фактически
это имя может быть заменено абсолютно произвольным текстом,
не обязательно цифрами

#DEFINE LOWER 0/* LOWER LIMIT OF TABLE */
#DEFINE UPPER 300 /* UPPER LIMIT */
#DEFINE STEP 20 /* STEP SIZE */
MAIN () /* FAHRENHEIT-CELSIUS TABLE */
{
INT FAHR;
FOR (FAHR =LOWER; FAHR <= UPPER; FAHR =FAHR + STEP)
PRINTF("%4D %6.1F\N", FAHR, (5.0/9.0)*(FAHR-32));
}


величины LOWER, UPPER и STEP являются константами и поэ-
тому они не указываются в описаниях. Символические имена
обычно пишут прописными буквами, чтобы их было легко отли-
чить от написанных строчными буквами имен переменных. отме-
тим, что в конце определения не ставится точка с запятой.
Так как подставляется вся строка, следующая за определенным
именем, то это привело бы к слишком большому числу точек с
запятой в операторе FOR .




    1.5. Набор полезных программ




Теперь мы собираемся рассмотреть семейство родственных
программ, предназначенных для выполнения простых операций
над символьными данными. В дальнейшем вы обнаружите, что
многие программы являются просто расширенными версиями тех
прототипов, которые мы здесь обсуждаем.


    1.5.1. Ввод и вывод символов




Стандартная библиотека включает функции для чтения и за-
писи по одному символу за один раз. функция GETCHAR() извле-
кает следующий вводимый символ каждый раз, как к ней обраща-
ются, и возвращает этот символ в качестве своего значения.
Это значит, что после

C = GETCHAR()

переменная 'C' содержит следующий символ из входных данных.
Символы обычно поступают с терминала, но это не должно нас
касаться до главы 7.
Функция PUTCHAR(C) является дополнением к GETCHAR : в
результате обращения

PUTCHAR (C)

содержимое переменной 'C' выдается на некоторый выходной но-
ситель, обычно опять на терминал. Обращение к функциям
PUTCHAR и PRINTF могут перемежаться; выдача будет появляться
в том порядке, в котором происходят обращения.
Как и функция PRINTF , функции GETCHAR и PUTCHAR не со-
держат ничего экстраординарного. Они не входят в состав язы-
ка "C", но к ним всегда можно обратиться.


    1.5.2. Копирование файла




Имея в своем распоряжении только функции GETCHAR и
PUTCHAR вы можете, не зная ничего более об операциях вво-
да-вывода, написать удивительное количество полезных прог-
рамм. Простейшим примером может служить программа посимволь-
ного копирования вводного файла в выводной. Общая схема име-
ет вид:
ввести символ
WHILE (символ не является признаком конца файла)
вывести только что прочитанный символ
ввести новый символ

программа, написанная на языке "C", выглядит следующим обра-
зом:

MAIN() /* COPY INPUT TO OUTPUT; 1ST VERSION */
{
INT C;

C = GETCHAR();
WHILE (C != EOF) {
PUTCHAR (C);
C = GETCHAR();
}
}



оператор отношения != означает "не равно".
Основная проблема заключается в том, чтобы зафиксиро-
вать конец файла ввода. Обычно, когда функция GETCHAR натал-
кивается на конец файла ввода, она возвращает значение , не
являющееся действительным символом; таким образом, программа
может установить, что файл ввода исчерпан. Единственное ос-
ложнение, являющееся значительным неудобством, заключается в
существовании двух общеупотребительных соглашений о том, ка-
кое значение фактически является признаком конца файла. Мы
отсрочим решение этого вопроса, использовав символическое
имя EOF для этого значения, каким бы оно ни было. На практи-
ке EOF будет либо -1, либо 0, так что для правильной работы
перед программой должно стоять собственно либо

#DEFINE EOF -1

либо

#DEFINE EOF 0

Использовав символическую константу EOF для представле-
ния значения, возвращаемого функцией GETCHAR при выходе на
конец файла, мы обеспечили, что только одна величина в прог-
рамме зависит от конкретного численного значения.
Мы также описали переменную 'C' как INT , а не CHAR , с
тем чтобы она могла хранить значение, возвращаемое GETCHAR .
как мы увидим в главе 2, эта величина действительно INT, так
как она должна быть в состоянии в дополнение ко всем возмож-
ным символам представлять и EOF.
Программистом, имеющим опыт работы на "C", программа
копирования была бы написана более сжато. В языке "C" любое
присваивание, такое как

C = GETCHAR()

может быть использовано в выражении; его значение - просто
значение, присваиваемое левой части. Если присваивание сим-
вола переменной 'C' поместить внутрь проверочной части опе-
ратора WHILE , то программа копирования файла запишется в
виде:

MAIN() /* COPY INPUT TO OUTPUT; 2ND VERSION */
{
INT C;

WHILE ((C = GETCHAR()) != EOF)
PUTCHAR(C);
}

Программа извлекает символ , присваивает его переменной
'C' и затем проверяет, не является ли этот символ признаком
конца файла. Если нет - выполняется тело оператора WHILE,
выводящее этот символ. Затем цикл WHILE повторяется. когда,
наконец, будет достигнут конец файла ввода, оператор WHILE
завершается, а вместе с ним заканчивается выполнение и функ-
ции MAIN .



В этой версии централизуется ввод - в программе только
одно обращение к функции GETCHAR - и ужимается программа.
Вложение присваивания в проверяемое условие - это одно из
тех мест языка "C", которое приводит к значительному сокра-
щению программ. Однако, на этом пути можно увлечься и начать
писать недоступные для понимания программы. Эту тенденцию мы
будем пытаться сдерживать.
Важно понять , что круглые скобки вокруг присваивания в
условном выражении действительно необходимы. Старшинство
операции != выше, чем операции присваивания =, а это означа-
ет, что в отсутствие круглых скобок проверка условия != бу-
дет выполнена до присваивания =. Таким образом, оператор

C = GETCHAR() != EOF

эквивалентен оператору

C = (GETCHAR() != EOF)

Это, вопреки нашему желанию, приведет к тому, что 'C'
будет принимать значение 0 или 1 в зависимости от того, на-
толкнется или нет GETCHAR на признак конца файла. Подробнее
об этом будет сказано в главе 2/.


    1.5.3. Подсчет символов




Следующая программа подсчитывает число символов; она
представляет собой небольшое развитие программы копирования.

MAIN() /* COUNT CHARACTERS IN INPUT */
{
LONG NC;

NC = 0;
WHILE (GETCHAR() != EOF )
++NC;
PRINTF("%1D\N", NC);
}

Оператор

++NC;

демонстрирует новую операцию, ++, которая означает увеличе-
ние на единицу. Вы могли бы написать NC = NC + 1 , но ++NC
более кратко и зачастую более эффективно. Имеется соответст-
вующая операция -- уменьшение на единицу. Операции ++ и --
могут быть либо префиксными (++NC), либо постфиксными
(NC++); эти две формы, как будет показано в главе 2, имеют в
выражениях различные значения, но как ++NC, так и NC++ уве-
личивают NC. Пока мы будем придерживаться префиксных опера-
ций.



Программа подсчета символов накапливает их количество в
переменной типа LONG, а не INT . На PDP-11 максимальное зна-
чение равно 32767, и если описать счетчик как INT , то он
будет переполняться даже при сравнительно малом файле ввода;
на языке "C" для HONEYWELL и IBM типы LONG и INT являются
синонимами и имеют значительно больший размер. Спецификация
преобразования %1D указывает PRINTF , что соответствующий
аргумент является целым типа LONG .
Чтобы справиться с еще большими числами, вы можете ис-
пользовать тип DOUBLE / FLOAT двойной длины/. мы также ис-
пользуем оператор FOR вместо WHILE с тем, чтобы проиллюстри-
ровать другой способ записи цикла.

MAIN() /* COUNT CHARACTERS IN INPUT */
{
DOUBLE NC;

FOR (NC = 0; GETCHAR() != EOF; ++NC)
;
PRINTF("%.0F\N", NC);
}

Функция PRINTF использует спецификацию %F как для FLOAT
, так и для DOUBLE ; спецификация %.0F подавляет печать не-
существующей дробной части.
Тело оператора цикла FOR здесь пусто, так как вся рабо-
та выполняется в проверочной и реинициализационной частях.
Но грамматические правила языка "C" требуют, чтобы оператор
FOR имел тело. Изолированная точка с запятой, соответствую-
шая пустому оператору, появляется здесь, чтобы удовлетворить
этому требованию. Мы выделили ее на отдельную строку, чтобы
сделать ее более заметной.
Прежде чем мы распростимся с программой подсчета симво-
лов, отметим, что если файл ввода не содержит никаких симво-
лов, то условие в WHILE или FOR не выполнится при самом пер-
вом обращении к GETCHAR , и, следовательно , программа вы-
даст нуль, т.е. Правильный ответ. это важное замечание. од-
ним из приятных свойств операторов WHILE и FOR является то,
что они проверяют условие в начале цикла, т.е. До выполнения
тела. Если делать ничего не надо, то ничего не будет сдела-
но, даже если это означает, что тело цикла никогда не будет
выполняться. программы должны действовать разумно, когда они
обращаются с файлами типа "никаких символов". Операторы
WHILE и FOR помогают обеспечить правильное поведение прог-