
\chapter{Обзор языка}
\label{chapt:tutorial_intro}

Начнём с быстрого ознакомления с языком Си. Наша цель -- показать на реальных
программах существенные элементы языка, не вдаваясь в мелкие детали, формальные
правила и исключения из них. Поэтому мы не стремимся к полноте и даже точности
(заботясь, однако, о корректности примеров). Нам бы хотелось как можно скорее
подвести вас к моменту, когда вы сможете писать полезные программы, и, чтобы
сделать это, мы должны сконцентрировать внимание на основах: переменных и
константах, арифметике, управлении последовательностью вычислений, функциях и
простейшем вводе-выводе. В настоящей главе мы умышленно не затрагиваем тех
средств языка, которые важны при написании больших программ. Сюда входят
указатели, структуры, большая часть богатого набора операторов, несколько
управляющих инструкций и стандартная библиотека.

Такой подход имеет свои недостатки. Наиболее заметный из них связан с тем, что
мы не даём здесь законченного изложения свойств языка, и эта лаконичность может
привести к неправильному восприятию некоторых положений. В силу ограниченного
характера подачи материала в примерах не используется вся мощь языка, и потому
они не столь кратки и элегантны, как могли бы быть. Мы попытались по возможности
смягчить эти эффекты, но считаем необходимым предупредить о них. Другой
недостаток в том, что в последующих главах какие-то моменты нам придётся
повторить. Мы надеемся, что польза от повторений превысит вызываемое ими
раздражение.

В любом случае опытный программист, видимо, сможет материал данной главы
проэкстраполировать на свои программистские нужды. Новичкам же рекомендуем её
чтение дополнить написанием своих маленьких программ. И те и другие наши
читатели могут рассматривать эту главу как <<каркас>>, на который далее, начиная
с гл.~\ref{chapt:types-operators-expressions}, будут <<навешиваться>> элементы
языка.

\section{Начнём, пожалуй}

Единственный способ выучить новый язык программирования -- это писать на нём
программы. При изучении любого языка первой, как правило, предлагают написать
приблизительно следующую программу:

\begin{ShortCodeParWithCC}{\\\{\}}
\textit{Напечатайте слова}
    здравствуй, мир
\end{ShortCodeParWithCC}

\noindent%
\index{компиляция!Си-программ}%
Вот первое препятствие, и чтобы его преодолеть, вы должны суметь где-то создать
текст программы, успешно его скомпилировать, загрузить, пустить на счёт и
разобраться, куда будет отправлен результат. Как только вы овладеете этим, всё
остальное окажется относительно простым.

Си-программа, печатающая <<здравствуй, мир>>, выглядит так:

\begin{ShortCodePar}
#include <stdio.h>
main()
{
    printf ("здравствуй, мир\n");
}
\end{ShortCodePar}

Как запустить эту программу, зависит от системы, которую вы используете. Так, в
операционной системе UNIX необходимо сформировать исходную программу в файле с
именем, заканчивающимся символами <<.с>>, например, в файле \verb|hello.c|,
\index{cc@\texttt{cc}}%
который затем требуется скомпилировать с помощью команды

\begin{ShortCodePar}
cc hello.c
\end{ShortCodePar}

\noindent%
\index{a.out@\texttt{a.out}}%
Если вы всё сделали правильно -- не пропустили где-либо знака и не
допустили орфографических ошибок, то компиляция пройдёт <<молча>>, и вы получите
файл, готовый к исполнению и названный \verb|a.out|. Если вы теперь запустите
этот файл на счёт командой

\begin{ShortCodePar}
a.out
\end{ShortCodePar}

\noindent программа напечатает

\begin{ShortCodePar}
здравствуй, мир
\end{ShortCodePar}

\noindent В других системах правила запуска программы на выполнение могут быть
иными; чтобы узнать о них, поговорите со специалистами.

Теперь поясним некоторые моменты, касающиеся самой программы. Программа на Си,
каких бы размеров она ни была, состоит из \emph{функций} и
\emph{переменных}. Функции содержат \emph{инструкции}, описывающие
вычисления, которые необходимо выполнить, а переменные хранят значения,
используемые в процессе этих вычислений.

\index{функция}%
\index{функция!main@\texttt{main}}%
\index{main@\texttt{main}}%
Функции в Си похожи на подпрограммы и функции Фортрана и на процедуры и функции
Паскаля. Приведённая программа -- это функция с именем \verb|main|. Обычно вы
вольны придумывать любые имена для своих функций, но <<\verb|main|>> -- особое
имя: любая программа начинает свои вычисления с первой инструкции функции
\verb|main|.

Обычно \verb|main| для выполнения своей работы пользуется услугами других
функций; одни из них пишутся самим программистом, а другие берутся им готовыми
из имеющихся в его распоряжении библиотек.
\index{файл!головной!<stdio.h>@\texttt{<stdio.h>}}%
\index{stdio.h@\texttt{<stdio.h>}}%
Первая строка программы:

\begin{ShortCodePar}
#include <stdio.h>
\end{ShortCodePar}

\noindent сообщает компилятору, что он должен включить информацию о стандартной
библиотеке ввода-вывода. Эта строка встречается в начале многих исходных файлов
Си-программ. Стандартная библиотека описана в гл.~\ref{chapt:input-output} и
приложении~\ref{apx:stdlib}.

Один из способов передачи данных между функциями состоит в том, что функция при
обращении к другой функции передаёт ей список значений, называемых
\emph{аргументами}. Этот список обрамляется скобками и помещается после имени
функции. В нашем примере \verb|main| определена как функция, которая не ждёт
никаких аргументов. что отмечено пустым списком \verb|()|.

\FloatBarrier
\begin{center}
\begin{table}[H]
\hspace{-1\tabcolsep}\begin{tabular*}{\linewidth+2\tabcolsep}% умышленный overfull на \tabcolsep
{p{.5\linewidth-\tabcolsep}p{.5\linewidth-\tabcolsep}}
\multicolumn{2}{c}{ПЕРВАЯ ПРОГРАММА НА СИ}\\
\verb|#include <stdio.h>|
&\textit{Включение информации о стандартной библиотеке.}\\
\verb|main()| &
\textit{Определение функции с именем }\Verb|main|\textit{, не получающей никаких
аргументов.}\\
\verb|{|
&\multirow{2}={\begin{minipage}{\linewidth}\textit{Инструкции
}\Verb|main|\textit{ заключаются в фигурные скобки.}\end{minipage}}\\
&\\
\verb|    printf("здравствуй,мир\n");|
&\multirow{4}={\begin{minipage}{\linewidth}\textit{Функция }\Verb|main|\textit{
обращается к библиотечной функции }\Verb|printf|\textit{ для печати заданной
последовательности литер; }\Verb|\textbackslash n|\textit{ -- литера
новая-строка.}\end{minipage}}\\
&\\
\verb|}|&\\
&\\
\end{tabular*}
\end{table}
\end{center}

\index{фигурные скобки}%
Инструкции функции заключаются в фигурные скобки \verb|{ }|. Функция
\verb|main| содержит только одну инструкцию:

\begin{ShortCodePar}
printf ("здравствуй, мир\n");
\end{ShortCodePar}

\noindent Функция вызывается по имени, после которого, в скобках, указывается
список аргументов. Таким образом, приведённая строка -- это вызов функции
\verb|printf| с аргументом \verb|"здравствуй, мир\n"|. \verb|printf| --
\index{библиотечная функция}%
библиотечная функция, которая в данном случае напечатает последовательность
литер, заключённую в двойные кавычки.

\index{литера!двойная кавычка \texttt{\char`\"}}%
\index{константа!стринговая}%
Цепочка литер в двойных кавычках типа \verb|"здравствуй, мир\n"| называется
\emph{стрингом литер или стринговой константой}. Какое-то время в качестве
аргументов для \verb|printf| и других функций мы будем использовать только
стринги литер.

\index{литера!новая-строка \texttt{\textbackslash n}}%
\index{n@\texttt{\textbackslash n}}%
В Си комбинация \verb|\n| внутри стринга обозначает \emph{литеру новая-строка},
которая при печати вызывает переход к левому краю следующей строки. Если вы
удалите \verb|\n| (стоит поэкспериментировать), то обнаружите, что, закончив
печать, машина не переходит на новую строку. Литеру новая-строка в текстовый
аргумент \verb|printf| следует включать явным образом. Если вы попробуете
выполнить, например,

\begin{ShortCodePar}
printf ("здравствуй, мир
");
\end{ShortCodePar}

\noindent компилятор выдаст сообщение об ошибке.
 
Литера новая-строка никогда не вставляется автоматически, так что одну строку
можно напечатать по шагам с помощью нескольких обращений к \verb|printf|. Нашу
первую программу можно написать и так:

\begin{ShortCodePar}
#include <stdio.h>
main()
{
    printf ("здравствуй, ");
    printf ("мир");
    printf ("\n");
}
\end{ShortCodePar}

\noindent В результате её выполнения будет напечатана та же строка, что и
раньше.

\index{эскейп-последовательность}%
Заметим, что \verb|\n| обозначает только одну литеру. Такие особые комбинации
литер, начинающиеся с обратной наклонной черты, как \verb|\n|, и называемые
\emph{эскейп-последовательностями}, широко применяются для обозначения трудно
представимых или невидимых литер.
\index{литера!обратная наклонная черта \texttt{\textbackslash\textbackslash}}%
\index{b (backspace)@\texttt{\textbackslash b} (backspace)}%
Среди прочих в Си имеются литеры \verb|\t|, \verb|\b|, \verb|\"|, \verb|\\|,
обозначающие соответственно табуляцию, возврат на одну литеру назад (<<забой>>
последней литеры), двойную кавычку, саму наклонную черту. Полный список таких
литер представлен в разд.~\ref{sec:constants}.

\paragraph{Упражнение 1.1.} Выполните программу, печатающую ``здравствуй, мир'',
в вашей системе. Поэкспериментируйте, удаляя некоторые части программы, и
посмотрите, какие сообщения об ошибках вы получите.

\paragraph{Упражнение 1.2.} Выясните, что произойдёт, если в стринговую
константу аргумента \verb|printf| вставить \textbackslash\textit{c}, где
\textit{с} -- литера, не входящая в перечисленный выше список.

\section{Переменные и арифметические выражения}

\index{программа!преобразования температур}%
Следующая программа выполняет вычисления по формуле
${C^{\circ} =(5/9)(F^{\circ} -32)}$ и печатает приведённую ниже таблицу
соответствия температур по Фаренгейту и по Цельсию:

\begin{LongCodePar}
0   -17
20  -6
40  4
60  15
80  26
100 37
120 48
140 60
160 71
180 82
200 93
220 104
240 115
260 126
280 137
300 148
\end{LongCodePar}

\noindent Как и предыдущая, эта программа состоит из определения
одной-единственной функции \verb|main|. Она длиннее программы, печатающей
\verb|"здравствуй, мир"|, но не сложная. На ней мы продемонстрируем несколько
новых возможностей, включая комментарий, описания, переменные, арифметические
выражения, циклы и форматный вывод.

\begin{LongCodePar}
#include <stdio.h>

/* печать таблицы температур по Фаренгейту
   и Цельсию для fahr = 0, 20, ..., 300     */
main()
{
    int fahr, celsius;
    int lower, upper, step;
    
    lower = 0;   /* нижняя граница табл. температур */
    upper = 300; /* верхняя граница */
    step  = 20;  /* шаг */
    
    fahr = lower;
    while (fahr <= upper) {
        celsius = 5 * (fahr-32) / 9;
        printf("%d\t%d\n", fahr, celsius);
        fahr = fahr + step;
    }
}
\end{LongCodePar}

\noindent%
\index{комментарий}%
Две строки:

\begin{ShortCodePar}
/* печать таблицы температур по Фаренгейту
   и Цельсию для fahr = 0, 20, ..., 300     */
\end{ShortCodePar}

\noindent являются \emph{комментарием}, который в данном случае кратко
объясняет, что делает программа. Любые литеры, помещённые между \verb|/*| и
\verb|*/|, игнорируются компилятором, и ими можно свободно пользоваться, чтобы
сделать программу более понятной. Комментарий можно располагать в любом месте,
где могут стоять литеры пробела, табуляции или литера новая-строка.

В Си любая переменная должна быть описана раньше, чем она будет использована;
обычно все переменные описываются в начале функции перед первой исполняемой
инструкцией.
\index{декларация}%
В \emph{декларации (описании)} объявляются свойства переменных. Она состоит из
названия типа и списка переменных, как, например

\begin{ShortCodePar}
int fahr, celsius;
int lower, upper, step;
\end{ShortCodePar}

Тип \verb|int|, означает, что значения перечисленных переменных есть целые, в
отличие от него тип \verb|float| указывает на значения с плавающей точкой, т.е.
на числа, которые могут иметь дробную часть. Диапазоны значений обоих типов
зависят от машины.

\index{числа!размер}%
Числа типа \verb|int| бывают как 16-разрядные (они лежат в диапазоне от $-32768$
до $32767$), так и 32-разрядные Числа типа \verb|float| обычно представляются
32-разрядными словами, имеющими по крайней мере 6 десятичных значащих цифр, и
лежат приблизительно в диапазоне от $10^{-38}$ до $10^{+38}$.

\index{типы!базовые}%
\index{int@\texttt{int}!тип}%
Помимо \verb|int| и \verb|float| Си допускает ещё несколько базовых типов для
данных, это:

\begin{LongRtAlTab2cols}{\codeIndent}{0pt}{\Verb|double|}
\verb|char|%
\index{char@\texttt{char}!тип}%
&литера -- единичный байт\\
\verb|short|%
\index{short@\texttt{short}}%
&короткое целое\\
\verb|long|%
\index{long@\texttt{long}!тип}%
&длинное целое\\
\verb|double|%
\index{double@\texttt{double}!тип}%
&плавающее с двойной точностью
\end{LongRtAlTab2cols}

Размеры объектов указанных типов также зависят от машины.
\index{выводимые типы}%
\index{типы!выводимые}%
Из базовых типов можно создавать: \emph{массивы}, \emph{структуры} и
\emph{объединения}, \emph{указатели} на объекты базовых типов и функции,
возвращающие в качестве результата значения этих типов. Обо всём этом мы
расскажем позже.

\index{оператор!присваивания \texttt{=}}%
Вычисления в программе преобразования температур начинаются с \emph{инструкций
присваивания}:

\begin{ShortCodePar}
lower = 0;
upper = 300;
step = 20;
fahr = lower;
\end{ShortCodePar}

\noindent которые устанавливают указанным в них переменным их начальные
значения.
\index{инструкция!окончание}%
\index{точка с запятой}%
Любая инструкция заканчивается точкой с запятой.

Все строки таблицы вычисляются одним и тем же способом, поэтому мы воспользуемся
циклом, повторяющим это вычисление для каждой строки.
\index{while@\texttt{while}!инструкция}%
Необходимые действия выполнит цикл \verb|while|:

\begin{ShortCodePar}
while (fahr <= upper) {
    ...
}
\end{ShortCodePar}

\noindent Он работает следующим образом. Проверяется условие в скобках. Если оно
истинно (значение \verb|fahr| меньше или равно значению \verb|upper|), то тело
цикла (три инструкции, заключённые в фигурные скобки) выполняется. Затем опять
проверяется условие и, если оно истинно, то тело цикла выполняется снова. Когда
условие становится ложным (\verb|fahr| превысило \verb|upper|), цикл
завершается, и вычисления продолжаются с инструкции, которая следует за циклом.
Поскольку никаких инструкций за циклом нет, программа завершает работу.

\index{фигурные скобки}%
Телом цикла \verb|while| может быть одна или несколько инструкций, заключённых в
фигурные скобки, как в программе преобразования температур, или
одна-единственная инструкция без скобок, как в цикле

\begin{ShortCodePar}
while (i < j)
    i = 2 * i;
\end{ShortCodePar}

\noindent%
\index{отступы в тексте программы}%
\index{программа!формат}%
\index{программа!читаемость}%
И в том и в другом случае инструкции, находящиеся под управлением
\verb|while|, мы будем записывать со сдвигом, равным одной позиции табуляции,
которая в программе указывается четырьмя пробелами; благодаря этому будут ясно
видны инструкции, расположенные внутри цикла. Отступы подчёркивают логическую
структуру программы. Си-компилятор не обращает внимания на внешнее оформление
программы, но наличие в нужных местах отступов и пробелов существенно влияют на
то, насколько легко она будет восприниматься при её просмотре. Чтобы лучше была
видна логическая структура выражения, мы рекомендуем писать только по одной
инструкции на каждой строке и обрамлять пробелами знаки операций.
\index{расположение фигурных скобок}%
\index{фигурные скобки}%
\index{фигурные скобки!расположение}%
Положение скобок менее важно, хотя существуют различные точки зрения на этот
счёт. Мы остановились на одном из нескольких распространённых стилей их
применения. Выберите тот, который больше всего вам импонирует, и точно ему
следуйте.

Б\'{o}льшая часть вычислений выполняется в теле цикла. Температура переводится в
шкалу Цельсия и присваивается переменной \verb|celsius| посредством инструкции

\begin{ShortCodePar}
celsius = 5 * (fahr-32) / 9;
\end{ShortCodePar}

\noindent%
\index{деление целых}%
\index{оператор!деления \texttt{\slash}}%
Мы сначала умножаем на $5$ и затем делим на $9$, а не сразу умножаем на $5/9$.
\index{обрезание!при делении}%
Это связано с тем, что в Си, как и во многих других языках, деление целых
сопровождается \emph{обрезанием}, т.е. отбрасыванием дробной части. Так как $5$
и $9$ -- целые, обрезание $5/9$ дало бы нуль, и на месте температур по Цельсию
были бы напечатаны нули.

Этот пример ещё немного прибавил нам знаний о том, как работает \verb|printf|.
\verb|printf| -- универсальная функция форматного ввода-вывода, которая будет
подробно описана в гл.~\ref{chapt:input-output}, Её первый аргумент -- стринг,
в котором каждый знак \verb|%| соответствует одному из последующих её аргументов
(второму, третьему\ldots), а информация, расположенная за знаком \verb|%|,
указывает на вид, в котором каждый из этих аргументов выводится. Например,
\verb|%d| специфицирует выдачу аргумента в виде целого числа, и инструкция

\begin{ShortCodePar}
printf("%d\t%d\n", fahr, celsius);
\end{ShortCodePar}

\noindent печатает целое \verb|fahr|, выполняет табуляцию и печатает целое
\verb|celsius|.

В функции \verb|printf| каждому спецификатору первого аргумента (конструкции,
начинающейся с \verb|%|) соответствует второй аргумент, третий аргумент и т.д.
Спецификаторы и соответствующие им аргументы должны быть согласованы по
количеству и типам: в противном случае напечатано будет не то, что нужно.

Кстати, \verb|printf| не является частью языка Си, и вообще нет никаких
специальных конструкций языка, определяющих ввод-вывод. Функция \verb|printf|
-- лишь полезная функция стандартной библиотеки, которая обычно доступна для
Си-программ. Поведение функции \verb|printf|, однако, оговорено стандартом ANSI
и её свойства должны быть одинаковыми во всех Си-системах, удовлетворяющих
требованиям стандарта.

Желая сконцентрировать ваше внимание на самом Си, мы не будем много говорить о
вводе-выводе до гл.~\ref{chapt:input-output}. В частности, до этой главы мы
отложим разговор о форматном вводе. Если вам потребуется ввести числа, советуем
прочитать в разд.~\ref{sec:formatted-input} то, что касается функции
\verb|scanf|. Эта функция аналогична \verb|printf| с той лишь разницей, что она
вводит, а не выводит данные.

Существуют ещё две проблемы связанные с программой преобразования температур.
Одна из них (более простая) состоит в том, что выводимый результат выглядит
несколько неряшливо, поскольку числа не выровнены по правой позиции колонок. Это
легко исправить, добавив в каждый из спецификаторов \verb|%d| формата указание о
ширине поля; при этом программа будет печатать числа, прижимая их к правому краю
указанных полей. Например, мы можем написать

\begin{ShortCodePar}
printf("%3d %6d\n", fahr, celsius);
\end{ShortCodePar}

\noindent чтобы в каждой строке первое число печатать в поле из трёх позиций, а
второе -- из шести. В результате будет напечатано:

\begin{ShortCodePar}
  0    -17
 20     -6
 40      4
 60     15
 80     28
100     37
...
\end{ShortCodePar}

\index{программа!преобразования температур|(}%
Вторая, более серьёзная проблема связана с тем, что мы пользуемся целочисленной
арифметикой и поэтому не совсем точно вычисляем температуры по шкале Цельсия.
Например, $0^{\circ}F$ на самом деле (с точностью до десятой) равно
$-17.8^{\circ}C$‚ а не $-17$. Чтобы получить более точные значения температур,
нам надо пользоваться не целочисленной, а плавающей арифметикой. Это потребует
некоторых изменений в программе.

\begin{LongCodePar}
#include <stdio.h>

/* печать таблицы температур по Фаренгейту и Цельсию для
   fahr = 0, 20, ..., 300; вариант с плавающей точкой   */
main()
{
    float fahr, celsius;
    int lower, upper, step;
    
    lower = 0;   /* нижняя граница табл. температур */
    upper = 300; /* верхняя граница */
    step  = 20;  /* шаг */
    
    fahr = lower;
    while (fahr <= upper) {
        celsius = (5.0/9.0) * (fahr-32.0);
        printf("%3.0f %6.1f\n", fahr, celsius);
        fahr = fahr + step;
    }
}
\end{LongCodePar}

Программа мало изменилась. Она отличается от предыдущей лишь тем, что
\verb|fahr| и \verb|celsius| объявлены как \verb|float|, а формула
преобразования написана в более естественном виде. В предыдущем варианте нельзя
было писать \verb|5/9|, так как целочисленное деление в результате обрезания
дало бы нуль.
\index{константа!с плавающей точкой}%
Десятичная точка в константе указывает, что последняя рассматривается как число
с плавающей точкой, и \verb|5.0/9.0|‚ таким образом, есть частное от деления
двух значений с плавающей точкой, которое не предполагает отбрасывания дробной
части. В том случае, когда арифметическая операция имеет целые операнды, она
выполняется по правилам целочисленной арифметики.
\index{преобразование!целое -- плавающее}%
Если же один операнд плавающий, а другой -- целый, то перед тем, как операция
будет выполнена, последний будет преобразован в плавающий. Если бы мы написали
\verb|fahr-32|, то $32$ автоматически было бы преобразовано в число с плавающей
точкой. Тем не менее при записи констант с плавающей точкой мы всегда используем
десятичную точку, причём даже в тех случаях, когда константы на самом деле имеют
целые значения. Это делается для того, чтобы обратить внимание читающего
программу на их природу.

Более подробно правила, определяющие, в каких случаях целые переводятся в
плавающие, рассматриваются в гл.~\ref{chapt:types-operators-expressions}. А
сейчас заметим, что присваивание

\begin{ShortCodePar}
fahr = lower;
\end{ShortCodePar}

\noindent и проверка

\begin{ShortCodePar}
while (fahr <= upper)
\end{ShortCodePar}

\noindent работают естественным образом, т.е. перед выполнением операции
значение \verb|int| приводится к \verb|float|.

Спецификация \verb|%3.0f| в \verb|printf| определяет печать числа с плавающей
точкой (в данном случае числа \verb|fahr|) в поле шириной не более трёх позиций
без десятичной точки и дробной части. Спецификация \verb|%6.1f| описывает печать
другого числа (\verb|celsius|) в поле из шести позиций с одной цифрой после
десятичной точки. Напечатано будет следующее:

\begin{ShortCodePar}
 0  -17.8
20   -6.7
40    4.4
...
\end{ShortCodePar}

\noindent Ширину и точность можно не задавать: \verb|%6f| означает, что число
будет занимать не более шести позиций; \verb|%.2f| -- число имеет две цифры
после десятичной точки, но ширина не ограничена; \verb|%f| просто указывает на
печать числа с плавающей точкой.

\begin{LongRtAlTab2cols}{2\parindent-\codeIndent}{0pt}{\Verb|\%6.2f|}
\verb|%d|   &печать десятичного целого \\
\verb|%6d|  &печать десятичного целого в поле из 6 позиций \\
\verb|%f|   &печать числа с плавающей точкой \\
\verb|%6f|  &печать числа с плавающей точкой в поле из 6 позиций\\
\verb|%.2f| &печать числа с плавающей точкой с 2 цифрами после
десятичной точки \\
\verb|%6.2f|&печать числа с плавающей точкой в поле из 6 позиций и 2 цифрами
после десятичной точки
\end{LongRtAlTab2cols}

\noindent Кроме того, \verb|printf| допускает также следующие спецификаторы:
\verb|%o| для восьмеричного числа, \verb|%x| для шестнадцатеричного числа,
\verb|%c| для литеры, \verb|%s| для стринга литер и \verb|%%| для самого \%.
%
% исправлена орфографическая ошибка ``шестнадцатиричного''
%

\paragraph{Упражнение 1.3.} Усовершенствуйте программу преобразования температур
таким образом, чтобы над таблицей она печатала заголовок.

\paragraph{Упражнение 1.4.} Напишите программу, которая будет печатать таблицу
соответствия температур по Цельсию температурам по Фаренгейту.

\section{Инструкция \texorpdfstring{\protect\Verb|for|}{for}}

\index{for@\texttt{for}!инструкция}%
Существует много разных способов для написания одной и той же программы.
Видоизменим нашу программу преобразования температур:

\begin{LongCodePar}
#include <stdio.h>
/* печать таблицы температур по Фаренгейту и Цельсию */
main()
{
    int fahr;
    for (fahr = 0; fahr <= 300; fahr = fahr + 20)
        printf("%3d %6.1f\n", fahr, (5.0/9.0)*(fahr-32));
}
\end{LongCodePar}

\noindent Эта программа печатает тот же результат, но выглядит она, несомненно,
по-другому. Главное отличие в отсутствии большинства переменных. Осталась только
переменная \verb|fahr|, которую мы описали как \verb|int|. Нижняя и верхняя
границы и шаг присутствуют в виде констант в инструкции \verb|for| -- новой для
нас конструкции, а выражение, вычисляющее температуру по Цельсию, теперь задано
третьим аргументом функции \verb|printf|, а не в отдельной инструкции
присваивания.

Это последнее изменение является примером применения общего правила: в любом
контексте, где возможно использование значения переменной какого-то типа, можно
использовать более сложное выражение того же типа. Так, на месте третьего
аргумента функции \verb|printf| согласно спецификатору \verb|%6.1f| должно быть
значение с плавающей точкой, следовательно, здесь может быть любое выражение
этого типа.

Инструкция \verb|for| описывает цикл, который является обобщением цикла
\verb|while|. Если вы сравните его с ранее написанным \verb|while|, то вам
станет ясно, как он работает. Внутри скобок имеются три выражения, разделяемых
точкой с запятой. Первое выражение -- инициализация

\begin{ShortCodePar}
fahr = 0
\end{ShortCodePar}

\noindent -- выполняется один раз перед тем, как войти в цикл. Второе --
проверка условия продолжения цикла

\begin{ShortCodePar}
fahr <= 300
\end{ShortCodePar}

\noindent Условие вычисляется, и, если оно истинно, тело цикла (в нашем случае
это одно обращение к \verb|printf|) выполняется. Затем осуществляется приращение
шага:

\begin{ShortCodePar}
fahr = fahr + 20
\end{ShortCodePar}

\noindent и условие вычисляется снова. Цикл заканчивается, когда условие
становится ложным. Как и в случае с \verb|while|‚ тело \verb|for|-цикла может
состоять из одной инструкции или из нескольких, заключённых в фигурные скобки.
На месте этих трёх выражений (инициализации, условия и приращения шага) могут
стоять произвольные выражения.

\index{for\texttt{for}!вместо \texttt{while}}%
\index{while@\texttt{while}!вместо \texttt{for}}%
Выбор между \verb|while| и \verb|for| определяется соображениями ясности
программы. Цикл \verb|for| более удобен в тех случаях, когда инициализация и
приращение шага логически связаны друг с другом общей переменной и выражаются
единичными инструкциями, поскольку он компактнее цикла \verb|while|, а его
управляющие части сосредоточены в одном месте.
\index{программа!преобразования температур|)}%


\paragraph{Упражнение 1.5.} Измените программу преобразования температур так,
чтобы она печатала таблицу в обратном порядке, т.е. от $300$ до $0$.

\section{Именованные константы}

Прежде чем мы закончим рассмотрение программы преобразования температур,
выскажем ещё одно соображение.
\index{магические числа}%
Очень плохо, когда по программе рассеяны <<магические числа>> типа $300$, $20$.
Для того кто будет читать программу, в них нет и намёка на то, что они собой
представляют. Кроме того, их трудно заменить на другие каким-то систематическим
способом. Одна из возможностей справиться с такими числами -- дать им
осмысленные имена.
\index{define@\texttt{{\#}define}}%
Строка \verb|#define| определяет \emph{символьное имя}, или
\emph{именованную константу}, для заданного стринга литер:

\begin{ShortCodeParWithCC}{\\\{\}}
#define   \textit{имя}   \textit{подставляемый-текст}
\end{ShortCodeParWithCC}

\noindent С этого момента при любом появлении имени (если только оно встречается
не в тексте, заключённом в кавычки, и не является частью определения другого
имени) оно будет заменяться на соответствующий ему \textit{подставляемый-текст}.
Имя имеет тот же вид, что и переменная: последовательность букв и цифр,
начинающаяся с буквы. \textit{Подставляемый-текст} может быть любой
последовательностью литер, среди которых могут встречаться не только цифры.

\begin{LongCodePar}
#include <stdio.h>
#define   LOWER  0       /* нижняя граница таблицы */
#define   UPPER  300     /* верхняя граница */
#define   STEP   20      /* размер шага */
/* печать таблицы температур по Фаренгейту и Цельсию */
main()
{
    int fahr;
    for (fahr = LOWER; fahr <= UPPER; fahr = fahr + STEP)
        printf("%3d %6.1f\n", fahr, (5.0/9.0)*(fahr-32));
}
\end{LongCodePar}

\noindent Величины \verb|LOWER|, \verb|UPPER| и \verb|STEP| -- именованные
константы, а не переменные. Поэтому для них нет описаний. По общепринятому
соглашению имена именованных констант набираются заглавными буквами, чтобы они
отличались от обычных переменных, набираемых строчными.
\index{точка с запятой}%
Заметим, что в конце \verb|#define|-строки точка с запятой не ставится.

\section{Ввод-вывод литер}

\index{литера!ввод-вывод}%
\index{ввод!с клавиатуры}%
Теперь мы намерены рассмотреть семейство программ по текстовой обработке. Вы
обнаружите, что многие реальные программы являются просто более развитыми
версиями описываемых здесь программ.

Стандартная библиотека поддерживает очень простую модель
\index{ввод-вывод!литер}%
ввода-вывода.
\index{поток!текстовый}%
\index{текстовый поток}%
Текстовый ввод-вывод вне зависимости от того, откуда он исходит или куда
направляется, имеет дело с потоком литер.
\index{литера!новая-строка \texttt{\textbackslash n}}%
\index{n@\texttt{\textbackslash n}}%
\emph{Текстовый поток} -- это последовательность литер, разбитая на строки,
каждая из которых содержит нуль или более литер и завершается литерой
новая-строка. Обязанность следить за тем, чтобы любой поток ввода-вывода отвечал
этой модели, возложена на библиотеку: программист, пользуясь библиотекой, не
должен заботиться о том, в каком виде строки представляются вне программы.

Стандартная библиотека включает несколько функций для чтения и записи одной
литеры. Простейшие из них --
\index{библиотечная функция!getchar@\texttt{getchar}}%
\verb|getchar| и
\index{библиотечная функция!putchar@\texttt{putchar}}%
\verb|putchar|. За одно обращение к \verb|getchar| читается \emph{следующая
литера ввода} из текстового потока, которая и выдаётся в качестве результата.
Так, после выполнения

\begin{ShortCodePar}
c = getchar();
\end{ShortCodePar}

\noindent переменная \verb|c| содержит следующую литеру ввода. Обычно
\index{ввод-вывод!с терминала}%
литеры поступают с клавиатуры. Ввод из файлов рассматривается в
гл.~\ref{chapt:input-output}.

Обращение к \verb|putchar| приводит к печати одной литеры. Так,

\begin{ShortCodePar}
putchar(c);
\end{ShortCodePar}

\noindent напечатает содержимое целой переменной \verb|c| в виде
\index{вывод!на экран}%
литеры (обычно на экране). Вызовы \verb|putchar| и \verb|printf| могут
произвольным образом перемежаться. Вывод будет формироваться в том же порядке, в
каком осуществляются обращения к этим функциям.

\subsection{Копирование файла}

\index{программа!копирования файлов|(}%
При наличии функций \verb|getchar| и \verb|putchar|, ничего больше не зная о
вводе-выводе, можно написать удивительно много полезных программ. Простейший
пример -- это программа, копирующая по одной литере с входного потока в
выходной поток

\begin{ShortCodeParWithCC}{\\\{\}}
\textit{чтение литеры}
while (\textit{литера не есть признак-конца-файла})
    \textit{вывод только что прочитанной литеры}
    \textit{чтение литеры}
\end{ShortCodeParWithCC}

\noindent Оформляя её в виде программы на Си, получим

\begin{LongCodePar}
#include <stdio.h>
/* копирование ввода на вывод; 1-я версия */
main()
{
    int c;
    c = getchar();
    while (c != EOF) {
        putchar(c);
        c = getchar();
    }
}
\end{LongCodePar}

\noindent%
\index{оператор!неравенства \texttt{"!=}}%
\index{операторы!отношения}%
Оператор отношения \verb|!=| означает <<не равно>>.

Каждая литера, вводимая с клавиатуры или появляющаяся на экране, как и любая
другая литера внутри машины, кодируется комбинацией бит. Тип \verb|char|
специально предназначен для хранения литерных данных, однако для этого также
годится и любой целый тип. Мы пользуемся типом \verb|int| и делаем это по одной
важной причине, которая требует разъяснений.

Существует проблема: как отличить конец ввода от обычных читаемых данных.
Решение в том, чтобы функция \verb|getchar| по исчерпании входного потока
выдавала в качестве результата такое значение, которое нельзя было бы спутать ни
с одной реальной литерой. Это значение есть \verb|EOF| (аббревиатура от end of
file -- конец файла). Мы должны объявить переменную \verb|c| такого типа, чтобы
его <<хватило>> для представления всех возможных результатов, выдаваемых
функцией \verb|getchar|. Нам не подходит тип \verb|char|, так как \verb|c|
должна быть достаточно <<ёмкой>>, чтобы помимо любого значения типа \verb|char|
быть в состоянии хранить и \verb|EOF|. Вот почему мы используем \verb|int|, а не
\verb|char|.

\index{файл!головной!<stdio.h>@\texttt{<stdio.h>}}%
\index{EOF@\texttt{EOF}}%
\index{stdio.h@\texttt{<stdio.h>}}%
\verb|EOF| -- целая константа, определённая в \verb|<stdio.h>|. Какое значение
имеет эта константа -- неважно, лишь бы оно отличалось от любого из возможных
значений типа \verb|char|. Использование именованной константы с унифицированным
именем гарантирует, что программа не будет зависеть от конкретного числового
значения. которое, возможно, в других Си-системах будет иным.

\index{инструкции!присваивания вложенные}%
Программу копирования можно написать более сжато. В Си любое
\index{выражение!присваивание}%
\index{присваивание!выражение}%
присваивание, например,

\begin{ShortCodePar}
c = getchar();
\end{ShortCodePar}

\noindent трактуется как выражение со значением, равным значению левой части
после присваивания.
\index{присваивание!инструкция вложенная}%
Это значит, что присваивание может встречаться внутри более
сложного выражения. Если присваивание переменной \verb|c| расположить в проверке
условия цикла \verb|while|, то программу копирования можно будет записать в
следующем виде:

\begin{LongCodePar}
#include <stdio.h>
/* копирование ввода на вывод; 2-я версия */
main()
{
    int c;
    while ((c = getchar()) != EOF)
        putchar(c);
}
\end{LongCodePar}

Цикл \verb|while|, пересылая в \verb|c| полученное от \verb|getchar| значение,
сразу же проверяет: не является ли оно <<концом файла>>. Если это не так, тело
цикла \verb|while| выполняется и литера печатается. По окончании ввода
завершается работа цикла \verb|while| и тем самым и \verb|main|.

В данной версии ввод <<централизован>> -- в программе имеется только одно
обращение к \verb|getchar|. В результате она более компактна и легче
воспринимается при чтении. Вам часто придётся сталкиваться с такой формой
записи, где присваивание делается вместе с проверкой. (Чрезмерное увлечение ею,
однако, может запутать программу, поэтому мы постараемся пользоваться указанной
формой разумно.)

\index{оператор!приоритет}%
\index{приоритеты операторов}%
Скобки внутри условия, обрамляющие присваивание, необходимы. \emph{Приоритет}
\verb|!=| выше, чем приоритет \verb|=|, из чего следует, что при отсутствии
скобок проверка \verb|!=| будет выполняться до операции присваивания \verb|=|.
Таким образом, запись

\begin{ShortCodePar}
c = getchar() != EOF
\end{ShortCodePar}

\noindent эквивалентна записи

\begin{ShortCodePar}
c = (getchar() != EOF)
\end{ShortCodePar}

\noindent А это совсем не то, что нам нужно: переменной \verb|c| будет
присваиваться $0$ или $1$ в зависимости от того, встретит или не встретит
\verb|getchar| признак конца файла. (Более подробно об этом см. в
гл.~\ref{chapt:types-operators-expressions}.)
\index{программа!копирования файлов|)}%

\paragraph{Упражнение 1.6.} Убедитесь в том, что выражение
\verb|getchat() != EOF| получает значение $0$ или $1$.

\paragraph{Упражнение 1.7.} Напишите программу, печатающую значение
\verb|EOF|.

\subsection{Подсчёт литер}

\index{программа!подсчёта!литер}%
Следующая программа занимается подсчётом литер; она имеет много сходных черт с
программой копирования.

\begin{LongCodePar}
#include <stdio.h>
/* подсчёт вводимых литер; 1-я версия */
main()
{
    long nc;
    nc = 0;
    while (getchar() != EOF)
        ++nc;
    printf("%1d\n", nc);
}
\end{LongCodePar}

\index{оператор!инкрементации \texttt{++}}%
Инструкция

\begin{ShortCodePar}
++nc
\end{ShortCodePar}

\noindent демонстрирует новый оператор \verb|++|, который означает
\emph{увеличить на единицу}. Вместо этого можно было бы написать
\verb|nc = nc+1|, но \verb|++nc| намного короче, а часто и эффективнее.
\index{оператор!декрементации \texttt{\textminus\textminus}}%
Существует аналогичный оператор \verb|--|‚ означающий \emph{уменьшить на
единицу}. Операторы \verb|++| и \verb|--| могут быть и префиксными (\verb|++nc|)
и постфиксными (\verb|nc++|). Как будет показано в
гл.~\ref{chapt:types-operators-expressions}, эти две формы имеют разные значения
в выражениях, но и \verb|++nc|, и \verb|nc++| добавляют к \verb|nc| единицу. В
данном случае мы остановились на префиксной записи.

\index{граничные условия}%
Программа подсчёта литер накапливает сумму в переменной типа \verb|long|.
\index{числа!размер}%
\index{long@\texttt{long}!тип}%
Целые типа \verb|long| имеют не менее 32 бит. Хотя на некоторых машинах типы
\verb|int| и \verb|long| имеют одинаковый размер, существуют, однако машины, в
которых \verb|int| занимает 16 бит с максимально возможным значением $32767$, а
это -- сравнительно маленькое число, и счётчик типа \verb|int| может
переполниться.
\index{ld преобразование@\texttt{{\%}ld} преобразование}%
Спецификация преобразования \verb|%ld| в \verb|printf| указывает,
что соответствующий аргумент имеет тип \verb|long|.

\index{double@\texttt{double}!тип}%
\index{float@\texttt{float}!тип}%
Возможно охватить ещё больший диапазон значений, если использовать тип
\verb|double| (т.е. \verb|float| с двойной точностью). Применим также
инструкцию \verb|for| вместо \verb|while|, чтобы продемонстрировать другой
способ написания цикла.

\begin{LongCodePar}
#include <stdio.h>
/* подсчёт вводимых литер; 2-я версия */
main()
{
    double nc;
    for (nc = 0; getchar() != EOF; ++nc)
        ;
    printf("%.0f\n", nc);
}
\end{LongCodePar}

\noindent В \verb|printf| спецификатор \verb|%f| применяется как для
\verb|float|, так и для \verb|double|; спецификатор \verb|%.0f| означает печать
без десятичной точки и дробной части (последняя в нашем случае отсутствует).

\index{for@\texttt{for}!инструкция}%
Тело указанного \verb|for|-цикла пусто, поскольку кроме проверок и приращений
счётчика делать ничего не нужно. Но правила грамматики Си требуют, чтобы
\verb|for|-цикл имел тело.
\index{инструкция!пустая}%
\index{точка с запятой}%
Выполнение этого требования обеспечивает изолированная точка с запятой,
называемая \emph{пустой инструкцией}. Мы поставили точку с запятой на отдельной
строке для большей наглядности.

Наконец, заметим, что, если ввод не содержит ни одной литеры, то при первом же
обращении к \verb|getchar| условие в \verb|while| или \verb|for| не будет
выполнено, и программа выдаст нуль, что и будет правильным результатом. Это
важно. Одно из привлекательных свойств циклов \verb|while| и \verb|for| состоит
в том, что условие проверяется до того, как выполняется тело цикла. Если ничего
делать не надо, то ничего делаться и не будет, пусть даже тело цикла не будет
выполнено ни разу. Программа должна вести себя корректно при нулевом количестве
вводимых литер. Само устройство циклов \verb|while| и \verb|for| даёт
дополнительную уверенность в правильном поведении программы в случае граничных
условий.

\subsection{Подсчёт строк}

\index{программа!подсчёта!строк}%
Следующая программа подсчитывает строки. Как упоминалось выше, стандартная
библиотека обеспечивает такую модель ввода-вывода, при которой входной текстовый
поток состоит из последовательности строк, каждая из которых заканчивается
литерой новая-строка. Следовательно, подсчёт строк сводится к подсчёту числа
литер новая-строка.

\begin{LongCodePar}
#include <stdio.h>
/* подсчёт строк входного потока */
main()
{
    int c, nl;
    nl=0;
    while ((0 = getchar()) != EOF)
        if (c == '\n')
            ++nl;
    printf("%d\n", nl);
}
\end{LongCodePar}

Тело цикла теперь образует инструкция \verb|if|, под контролем которой находится
увеличение счётчика \verb|nl| на единицу.
\index{if-else@\texttt{if-else}!инструкция}%
Инструкция \verb|if| проверяет условие в скобках и, если оно истинно, выполняет
следующую за ним инструкцию (или группу инструкций, заключённую в фигурные
скобки).
\index{отступы в тексте программы}%
\index{программа!формат}%
Мы опять делаем отступы в тексте программы, чтобы показать, что чем управляется.

\index{оператор!равенства \texttt{==}}%
Двойной знак равенства в языке Си обозначает оператор <<равно>> (он аналогичен
оператору \verb|=| в Паскале и \verb|.EQ| в Фортране). Удваивание \verb|=| в
операторе проверки на равенство сделано для того, чтобы отличить его от
единичного \verb|=|, используемого в Си для обозначения присваивания.
Предупреждаем: начинающие программировать на Си иногда пишут \verb|=|, а имеют в
виду \verb|==|. Как мы увидим в гл.~\ref{chapt:types-operators-expressions}, в
этом случае результатом обычно будет вполне допустимое по форме выражение, на
которое компилятор не выдаст никаких предупреждающих сообщений.

\index{литера!кавычка \texttt{'}}%
Литера‚ заключённая в одиночные кавычки, представляет целое значение, равное
коду этой литеры (в кодировке, принятой на данной машине).
\index{константа!литерная}%
Это так называемая \emph{литерная константа}. Существует и другой способ для
написания маленьких целых значений. Например, \verb|'A'| есть литерная
константа;
\index{литеры!набор!ASCII}%
\index{ASCII}%
в наборе литер ASCII её значение равняется $65$ -- внутреннему представлению
буквы A. Конечно, \verb|'A'| в роли константы предпочтительнее, чем $65$,
поскольку смысл первой записи более очевиден, и она не зависит от конкретного
способа кодировки литер.

\index{эскейп-последовательность}%
Эскейп-последовательности, используемые в стринговых константах, допускаются
также и в литерных константах.
\index{литера!новая-строка \texttt{\textbackslash n}}%
\index{n@\texttt{\textbackslash n}}%
Так, \verb|'\n'| обозначает код литеры новая-строка, который в ASCII равен $10$.
\index{константа!стринговая}%
Следует обратить особое внимание на то, что \verb|'\n'| обозначает одну литеру
(код которой в выражении рассматривается как целое значение в то время как
\verb|"\n"| -- стринговая константа, в которой чисто случайно указана одна
литера. Более подробно различие между литерными и стринговыми константами
разбирается в гл.~\ref{chapt:types-operators-expressions}.

\paragraph{Упражнение 1.8.} Напишите программу для подсчёта пробелов, табуляций
и новых-строк.

\paragraph{Упражнение 1.9.} Напишите программу, копирующую литеры ввода в
выходной поток и заменяющую подряд стоящие пробелы на один пробел.

\paragraph{Упражнение 1.10.} Напишите программу, копирующую вводимые литеры в
выходной поток с заменой литеры табуляции на \verb|\t|, литеры забоя на
\verb|\b| и каждой обратной наклонной черты на \verb|\\|. Это сделает видимыми
все литеры табуляции и забоя.

\subsection{Подсчёт слов}

\index{программа!подсчёта!слов}%
Четвёртая из нашей серии полезных программ подсчитывает строки, слова и литеры,
причём под словом здесь имеется в виду любой стринг литер, не содержащий в себе
пробелов, табуляций и новых-строк. Эта программа является упрощённой версией
программы wc системы UNIX.

\begin{LongCodePar}
#include <stdio.h>
#define IN  1    /* внутри слова */
#define OUT 0    /* вне слова */
/* подсчёт строк, слов и литер */
main()
{
    int c, nl, nw, nc, state;
    state = OUT;
    nl = nw = nc = 0;
    while ((c = getchar()) != EOF) {
        ++nc;
        if (c == '\n')
            ++nl;
        if (c == ' ' || c == '\n' || c == '\t')
            state = OUT;
        else if (state == OUT) {
            state = IN;
            ++nw;
        }
    }
    printf("%d %d %d\n", nl, nw, nc);
}
\end{LongCodePar}

\noindent Каждый раз, встречая первую литеру слова, программа изменяет значение
счётчика слов на $1$. Переменная \verb|state| фиксирует текущее состояние --
находимся мы внутри или вне слова. Вначале ей присваивается значение \verb|OUT|,
что соответствует состоянию <<вне слова>>. Мы предпочитаем пользоваться
именованными константами \verb|IN| и \verb|OUT|, а не собственно значениями $1$
и $0$, чтобы сделать программу более понятной. В нашей маленькой программе этот
приём мало что даёт, но в большой программе увеличение её ясности окупает
незначительные дополнительные усилия, потраченные на то, чтобы писать программу
в таком стиле с самого начала. Вы обнаружите, что большие изменения гораздо
легче вносить в те программы, в которых магические числа встречаются только в
виде именованных констант.

\index{множественное присваивание}%
\index{инструкции!присваивания вложенные}%
\index{присваивание!множественное}%
\index{присваивание!выражение}%
Строка

\begin{ShortCodePar}
nl = nw = nc = 0;
\end{ShortCodePar}

\noindent устанавливает все три переменные в нуль. Такая запись не является
какой-то особой конструкцией и допустима потому, что
\index{выражение!присваивание}%
\index{присваивание!инструкция вложенная}%
присваивание есть выражение со своим собственным значением и
\index{вычисление, порядок}%
\index{порядок!вычислений}%
операции присваивания выполняются справа налево. Указанная строка эквивалентна

\begin{ShortCodePar}
nl = (nw = (nc = 0));
\end{ShortCodePar}

\index{оператор!логического ИЛИ \texttt{"|"|}}%
Оператор \verb+||+ означает ИЛИ, так что строка

\begin{ShortCodePar}
if (c == ' ' || c == '\n' || c == '\t')
\end{ShortCodePar}

\noindent читается как <<если \verb|c| есть пробел, \emph{или} \verb|c| есть
новая-строка, \emph{или} \verb|c| есть табуляция>>. (Напомним, что
эскейп-последовательность \verb|\t| обозначает литеру табуляции.)
\index{вычисление, порядок}%
\index{порядок!вычислений}%
\index{оператор!логического И \texttt{\&\&}}%
Существует также оператор \verb|&&|, означающий И. Его приоритет выше, чем
приоритет \verb+||+. Выражения, связанные операторами \verb|&&| или \verb+||+,
вычисляются слева направо; при этом гарантируется, что вычисления сразу
прервутся, как только будет установлена истинность или ложность условия. Если
\verb|c| есть пробел, то дальше проверять, является значение \verb|c| литерой
новая-строка или же табуляции, не нужно. В этом частном случае данный способ
вычислений не столь важен, но он имеет значение в более сложных ситуациях,
которые мы вскоре рассмотрим.

\index{if-else@\texttt{if-else}!инструкция}%
В примере также встречается слово \verb|else|, которое указывает на
альтернативные действия, выполняемые в случае, когда условие, указанное в
\verb|if|, не является истинным. В общем виде условная инструкция записывается
так:

\begin{ShortCodeParWithCC}{\\\{\}}
if (\textit{выражение})
    \textit{инструкция}\textsubscript{1}
else
    \textit{инструкция}\textsubscript{2}
\end{ShortCodeParWithCC}

\noindent В конструкции \verb|if-else| выполняется одна и только одна из двух
инструкций. Если \textit{выражение} истинно, то выполняется
\textit{инструкция}$_{1}$, если нет, то -- \textit{инструкция}$_{2}$. Каждая из
этих двух \textit{инструкций} представляет собой либо одну инструкцию, либо
несколько, заключённых в фигурные скобки. В нашей программе после \verb|else|
стоит инструкция \verb|if|, управляющая двумя такими инструкциями.

\paragraph{Упражнение 1.11.} Как протестировать программу подсчёта слов? Какой
ввод вероятнее всего не обнаружит ошибок, если они были допущены?

\paragraph{Упражнение 1.12.} Напишите программу, которая печатает содержимое
своего ввода, помещая по одному слову на каждой строке.

\section{Массивы}

\index{программа!подсчёта!литер}%
\index{программа!подсчёта!пробельных литер}%
А теперь напишем программу, подсчитывающую по отдельности каждую цифру,
пробельные литеры (пробелы, табуляции и новые-строки) и все другие литеры. Это
несколько искусственная программа, но она позволит нам в одном примере
продемонстрировать ещё несколько возможностей языка Си. Имеется двенадцать
категорий вводимых литер. Удобно все десять счётчиков цифр хранить в массиве, а
не в виде десяти отдельных переменных. Вот один из вариантов этой программы:

\begin{LongCodePar}
#include <stdio.h>
/* подсчёт цифр, пробельных и прочих литер */
main()
{
    int c, i, nwhite, nother;
    int ndigit[1O];
    nwhite = nother = 0;
    for (i = 0; i < 10; ++i)
        ndigit[i] = 0;
    while ((0 = getchar()) != EOF)
        if (c >= '0' && c <= '9')
            ++ndigit[c-'0'];
        else if (c == ' ' || c == '\n' || c == '\t')
            ++nwhite;
        else
            ++nother;
    printf("цифры =");
    for (i = 0; i < 10; ++i)
        printf(" %d", ndigit[i]);
    printf(", пробелы = %d, прочие = %d\n", nwhite, nother);
}
\end{LongCodePar}

\noindent Если пропустить текст этой программы в качестве ввода, то будет
напечатан следующий результат

\begin{ShortCodePar}
цифры = 9 3 0 0 0 0 0 0 1, пробелы = 141, прочие = 347
\end{ShortCodePar}

\index{массив!декларация}%
\index{декларация!массива}%
Декларация

\begin{ShortCodePar}
int ndigit[10];
\end{ShortCodePar}

\noindent определяет \verb|ndigit| как массив из 10 значений типа \verb|int|. В
Си элементы массива всегда нумеруются, начиная с нуля, так что элементами этого
массива будут \verb|ndigit[0]|, \verb|ndigit[1]|‚ \ldots, \verb|ndigit[9]|, что
учитывается в \verb|for|-циклах (при инициализации и печати массива).

\index{массив!индексирование}%
\index{индексирование массива}%
Индексом может быть любое целое выражение, образуемое целыми переменными
(например, \verb|i|) и целыми константами.

\index{преобразование!литера -- целое}%
Приведённая программа опирается на определённые свойства кодировки цифр.
Например, проверка

\begin{ShortCodePar}
if (c >= '0' && c <= '9') ...
\end{ShortCodePar}

\noindent определяет, является ли находящаяся в \verb|c| литера цифрой. Если это
так, то

\begin{ShortCodePar}
c - '0'
\end{ShortCodePar}

\noindent есть числовое значение цифры. Сказанное справедливо только в том
случае, если для ряда значений \verb|'0'|‚ \verb|'1'|, \ldots, \verb|'9'| каждое
следующее значение на $1$ больше предыдущего. К счастью, это правило соблюдается
во всех наборах литер.

\index{преобразование!литера -- целое}%
По определению значения типа \verb|char| являются всего лишь малыми целыми, так
что переменные и константы типа: \verb|char| в арифметических выражениях
идентичны значениям типа \verb|int|. Это и естественно, и удобно: например,
\verb|c-'0'| есть целое выражение с возможными значениями от $0$ до $9$, которые
соответствуют литерам от  \verb|'0'| до \verb|'9'|, хранящимся в переменной
\verb|c|. Таким образом, значение данного выражения является правильным индексом
для массива \verb|ndigit|.

Следующий фрагмент определяет, является литера цифрой, пробельной литерой или
чем-нибудь иным.

\begin{ShortCodePar}
if (c >= '0' && c <= '9')
    ++ndigit[c-'0'];
else if (c == ' ' || c == '\n' || c == '\t')
    ++nwhite;
else
    ++nother
\end{ShortCodePar}
%%
%% в предыдущем блоке кода в оригинале не было точки с запятой в конце
%%

\noindent%
\index{многопутевое ветвление}%
\index{else-if@\texttt{else-if}}%
Конструкция вида

\begin{LongCodeParWithCC}{\\\{\}}
if (\textit{условие}\textsubscript{1})
    \textit{инструкция}\textsubscript{1}
else if (\textit{условие}\textsubscript{2})
    \textit{инструкция}\textsubscript{2}
...
    ...
else
    \textit{инструкция}\textsubscript{n}
\end{LongCodeParWithCC}

\noindent часто применяется для выбора одного из нескольких альтернативных
путей, имеющихся в программе. \textit{Условия} вычисляются по порядку в
направлении сверху вниз до тех пор, пока одно из них не будет удовлетворено; в
этом случае соответствующая ему \textit{инструкция} будет выполнена, и работа
всей конструкции завершится. (Любая из \textit{инструкций} может быть группой
инструкций, обрамленных фигурными скобками.) Если ни одно из \textit{условий} не
удовлетворено, выполняется последняя \textit{инструкция}, расположенная сразу за
\verb|else|, если таковая имеется. Если же \verb|else| и следующей за ней
\textit{инструкции} нет (как это было в программе подсчёта слов), то никакие
действия вообще не производятся.

Между первым \verb|if| и завершающим \verb|else| может быть сколько угодно
комбинаций вида

\begin{ShortCodeParWithCC}{\\\{\}}
else if (\textit{условие})
    \textit{инструкция}
\end{ShortCodeParWithCC}

\index{отступы в тексте программы}%
\index{программа!формат}%
Когда их несколько, программу разумно форматировать так, как мы здесь показали.
Если же каждый следующий \verb|if| сдвигать вправо относительно предыдущего
\verb|else|, то при длинном каскаде проверок текст окажется слишком близко
прижатым к правому краю страницы.

Инструкция \verb|switch| речь о которой пойдёт в гл.~\ref{chapt:control_flow},
обеспечивает другой способ изображения многопутевого ветвления на языке Си. Он
более подходит, в частности, тогда, когда условием перехода служит совпадение
значения некоторого выражения целочисленного типа с одной из констант, входящих
в заданный набор. Вариант нашей программы, реализованной с помощью
\verb|switch| приводится в разд.~\ref{sec:switch}.

\paragraph{Упражнение 1.13.} Напишите программу, печатающую гистограммы длин
вводимых слов. Гистограмму легко рисовать горизонтальными полосами. Рисование
вертикальными полосами -- более трудная задача.

\paragraph{Упражнение 1.14.} Напишите программу, печатающую гистограммы частот
встречаемости вводимых литер.

\section{Функции}

\index{модульность}%
\index{функция}%
Функции в Си играют ту же роль, что и подпрограммы и функции в Фортране или
процедуры и функции в Паскале. Функция обеспечивает удобный способ отдельно
оформить некоторое вычисление и пользоваться им далее, не заботясь о том, как
оно реализовано. После того как функции написаны можно забыть, \emph{как} они
сделаны, достаточно знать лишь, \emph{что} они умеют делать. Механизм
использования функций в Си удобен, лёгок и эффективен. Зачастую вы будете
встречать короткие функции, вызываемые только единожды; они оформлены в виде
функции с одной-единственной целью -- получить более ясную программу.

До сих пор мы пользовались готовыми функциями типа \verb|printf|,
\verb|getchar| и \verb|putchar|, теперь настала пора написать несколько наших
собственных функций. В Си нет оператора возведения в степень типа оператора
\verb|**| в Фортране.
\index{функция!power@\texttt{power}}%
Поэтому проиллюстрируем механизм определения функции на
примере функции \verb|power(m,n)|, которая возводит целое \verb|m| в целую
положительную степень \verb|n|. Так, \verb|power(2,5)| имеет значение $32$. На
самом деле для практического применения эта функция малопригодна, так как она
оперирует лишь малыми целыми степенями, однако она достаточно хороша, чтобы
послужить иллюстрацией. (В стандартной библиотеке есть функция \verb|pow(x,y)|,
вычисляющая $x^y$.)

Итак, мы имеем функцию \verb|power| и главную функцию, пользующуюся её услугами,
так что вся программа выглядит следующим образом:

\begin{LongCodePar}
#include <stdio.h>
int power(int m, int n);
/* тест функции power */
main()
{
    int i;
    
    for (i = 0; i < 10; ++i)
        printf("%d %d %d\n", i, power(2,i), power(-3,i));
    return 0;
}

/* возводит base в n-ю степень; n >= 0 */
int power(int base, int n)
{
    int i, p;
    
    p = 1;
    for (i = 1; i <= n; ++i)
        p = p * base;
    return p;
}
\end{LongCodePar}

\index{определение!аргумента}%
\index{определение!параметра}%
\index{определение!функции}%
\index{параметр!определение}%
\index{функция!определение}%
Определение любой функции имеет следующий вид:

\begin{ShortCodeParWithCC}{\\\[\]}
\textit[тип-результата]\
\textit[имя-функции](\textit[список параметров,\
если он есть])
{
    \textit[декларации]
    \textit[инструкции]
}
\end{ShortCodeParWithCC}

\noindent%
\index{компиляция!Си-программ}%
Определения функций могут располагаться в любом порядке в одном или в нескольких
исходных файлах, но любая функция должна быть целиком расположена в каком-то
одном. Если исходный текст программы распределён по нескольким файлам, то, чтобы
её скомпилировать и загрузить, вам придётся сказать несколько больше, чем в
случае одного файла; но это уже относится к операционной системе, а не к языку.
Пока мы предполагаем, что обе функции находятся в одном файле, так что тех
знаний, которые вы уже получили относительно запуска программ на Си, будет
достаточно.

В следующей строке из функции \verb|main| к \verb|power| обращаются дважды.

\begin{ShortCodePar}
printf("%d %d %d\n", i, power(2,i), power(-3,i));
\end{ShortCodePar}

\noindent При каждом вызове функции \verb|power| передаются два аргумента, и
каждый раз главная программа в ответ получает целое число, которое затем
приводится к должному формату и печатается. Как часть выражения
\verb|power(2,i)| представляет собой целое значение точно так же, как \verb|2|
или \verb|i|. (Не все функции в качестве результата выдают целые значения;
подробно об этом будет сказано позже, в
гл.~\ref{chapt:functions_and_program_structure}.)

В первой строке определения \verb|power|:

\begin{ShortCodePar}
int power(int base, int n)
\end{ShortCodePar}

\noindent указываются типы параметров, имя функции и тип результата. Имена
параметров локализованы в \verb|power|, и это значит, что они скрыты для любой
другой функции, так что остальные подпрограммы могут свободно пользоваться ими
для своих целей. Последнее утверждение справедливо также для переменных \verb|i|
и \verb|p|: \verb|i| в \verb|power| и \verb|i| в \verb|main| не имеют между
собой ничего общего.

\index{функция!аргумент}%
Далее \emph{параметром} мы будем называть переменную из списка параметров,
заключённого в скобки и заданного в определении функции, а \emph{аргументом} --
значение, используемое при обращении к функции. Иногда в том же смысле мы будем
употреблять термины \emph{формальный аргумент} и \emph{фактический аргумент}.

\index{return-инструкция@\texttt{return}-инструкция}%
Значение, вычисляемое функцией \verb|power|, возвращается в \verb|main| с
помощью инструкции \verb|return|. За словом \verb|return| может следовать любое
выражение:

\begin{ShortCodeParWithCC}{\\\{\}}
return \textit{выражение};
\end{ShortCodeParWithCC}

\noindent Функция не обязательно возвращает какое-нибудь значение. Инструкция
\verb|return| без выражения только передаёт управление в ту программу, которая
её вызвала, не передавая ей никакого результирующего значения. То же самое
происходит, если в процессе вычислений мы выходим на конец функции, отмеченный в
тексте последней закрывающей фигурной скобкой. Возможна ситуация, когда
вызывающая функция игнорирует возвращаемый ей результат.

\index{main@\texttt{main}!возврат из}%
\index{return@\texttt{return}!из \texttt{main}}%
Вы, вероятно, обратили внимание на инструкцию \verb|return| в конце \verb|main|.
Поскольку \verb|main| есть функция, как и любая другая она может вернуть
результирующее значение тому, кто её вызвал, -- фактически в
%%
%% в оригинале есть типографская строка
%% 2. Заказ № 13
%%
операционную среду, в которой была запущена программа. Обычно возвращается
нулевое значение, что говорит о нормальном завершении счёта. Ненулевое значение
сигнализирует о необычном или ошибочном завершении. До сих пор ради простоты мы
опускали \verb|return| в \verb|main|, но с этого момента будем задавать
\verb|return| как напоминание о том, что программы должны сообщать о состоянии
своего завершения в операционную систему.

\index{прототип функции}%
\index{функция!прототип}%
Декларация

\begin{ShortCodePar}
int power(int m, int n);
\end{ShortCodePar}

\noindent стоящая непосредственно перед \verb|main|, сообщает, что функция
\verb|power| ожидает двух целых аргументов и возвращает целый результат. Это
описание, называемое \emph{прототипом функции}, должно быть согласовано с
определением и всеми вызовами \verb|power|. Будет ошибкой, если определение
функции или вызов не соответствует своему прототипу.

Имена параметров не требуют согласования. Фактически в прототипе они могут быть
произвольными или вообще отсутствовать, т.е. прототип можно было бы записать и
так:

\begin{ShortCodePar}
int power(int, int);
\end{ShortCodePar}

\noindent Однако хорошо подобранные имена поясняют программу, и мы будем часто
этим пользоваться.

\index{декларация!функции!старым способом}%
\index{функция!в старом стиле}%
Историческое замечание. Самые большие отличия ANSI-Си от более ранних версий
языка как раз и заключаются в способах описания и определения функций. В первой
версии Си функцию \verb|power| требовалось задавать в следующем виде:

\begin{ShortCodePar}
/* power возводит base в n-ю степень; n >= 0 */
/* (версия в старом стиле языка Си)          */
power(base, n)
int base, n;
{
    int i, p;
    p = 1;
    for (i = 1; i <= n; ++i)
        p = p * base;
    return p;
}
\end{ShortCodePar}

\noindent Здесь имена параметров перечислены в круглых скобках, а их типы заданы
перед первой открывающей фигурной скобкой. В случае отсутствия указания о типе
параметра, считается, что он имеет тип \verb|int|. (Тело функции не претерпело
изменений.)

Описание \verb|power| в начале программы согласно первой версии Си должно было
бы выглядеть следующим образом:

\begin{ShortCodePar}
int power()
\end{ShortCodePar}

\noindent Нельзя было задавать список параметров, и поэтому компилятор не имел
возможности проверить правильность обращений к \verb|power|.
\index{декларация!функции!неявная}%
\index{функция!неявная декларация}%
Так как при отсутствии описания \verb|power| предполагалось, что функция
возвращает целое значение, то в данном случае описание целиком можно было бы
опустить.

Новый синтаксис для прототипов функций облегчает компилятору обнаружение ошибок
в количестве аргументов и их типах. Старый синтаксис описания и определения
функции всё ещё допускается стандартом ANSI, по крайней мере на переходный
период, но, если ваш компилятор поддерживает новый синтаксис, мы настоятельно
рекомендуем пользоваться только им.

\paragraph{Упражнение 1.15.} Перепишите программу преобразования температур,
выделив само преобразование в отдельную функцию.

\section{Аргументы. Вызов по значению}

Одно свойство функций в Си, вероятно, будет в новинку для программистов, которые
уже пользовались другими языками и Фортраном в частности.
\index{вызов!по значению}%
В Си все аргументы функции передаются <<по значению>>. Это следует понимать так,
что вызываемой функции посылаются значения её аргументов во временных
переменных, а не сами аргументы. Такой способ передачи аргументов несколько
отличается от <<вызова по ссылке>> в Фортране и спецификации \verb|var| при
параметре в Паскале, которые позволяют подпрограмме иметь доступ к самим
аргументам, а не к их локальным копиям.

Главное отличие в том, что в Си вызываемая функция не может непосредственно
изменить переменную вызывающей функции: она может изменить только её частную,
временную копию.

Однако вызов по значению следует отнести к достоинствам языка, а не к его
недостаткам. Благодаря этому свойству обычно удаётся написать более компактную
программу, содержащую меньшее число <<чужих>> переменных, поскольку параметры
можно рассматривать как должным образом инициализированные локальные переменные
вызванной подпрограммы.
\index{функция!power@\texttt{power}}%
В качестве примера приведём ещё одну версию функции
\verb|power|, в которой как раз использовано это свойство.

\begin{LongCodePar}
/* power возводит base в n-ю степень; n >= 0; версия 2 */
int power(int base, int n)
{
    int p;
    for (p = 1; n > 0; --n)
        p = p * base;
    return p;
}
\end{LongCodePar}

\noindent Параметр \verb|n| выступает здесь в роли временной переменной, в
которой циклом \verb|for| в убывающем порядке ведётся счёт числа шагов до тех
пор, пока её значение не станет нулём. При этом отпадает надобность в
дополнительной переменной \verb|i| в качестве счётчика цикла. Что бы мы ни
делали с \verb|n| внутри \verb|power|, это не окажет никакого влияния на сам
аргумент, копия которого была передана функции \verb|power| в её вызове.

\index{вызов!по ссылке}%
При желании можно сделать так, чтобы функция смогла изменить переменную в
вызывающей программе.
\index{переменная!адрес}%
Последняя для этого должна передать \emph{адрес}
подлежащей изменению переменной (указатель на переменную), а в вызываемой
функции следует соответствующий параметр описать как указатель и организовать
косвенный доступ через него к этой переменной. Все, что касается указателей, мы
рассмотрим в гл.~\ref{chapt:pointers_and_arrays}.

\index{массив!имя в роли аргумента}%
Механизм передачи в качестве аргумента массива -- несколько иной. Когда имя
массива используется как аргумент, то функции передаётся значение, которое
является адресом начала этого массива; никакие элементы массива не копируются. С
помощью индексирования
%%
%% в оригинале есть типографская строка
%% 2*
%%
относительно полученного значения функция имеет доступ к любому элементу
массива. Разговор об этом пойдёт в следующем разделе.

\section{Массивы литер}
\label{sec:char_arrays}

\index{массив!литер}%
Самый распространённый вид массива в Си -- массив литер.
\index{программа!печати!самой длинной строки}%
Чтобы проиллюстрировать использование литерных массивов и работающих с ними
функций, напишем программу, которая читает набор текстовых строк и печатает
самую длинную из них.

Её схема достаточно проста:

\begin{ShortCodeParWithCC}{\\\{\}}
while (\textit{есть ли ещё строка?})
    if (\textit{данная строка длиннее самой длинной из предыдущих})
        \textit{запомнить её}
        \textit{запомнить её длину}
\textit{напечатать самую длинную строку}
\end{ShortCodeParWithCC}

\noindent Из схемы видно, что программа естественным образом распадается на
части. Одна из них получает новую строку, другая проверяет её, третья
запоминает, а остальные управляют процессом вычислений.

\index{модульность}%
Поскольку процесс чётко распадается на части, его хорошо бы так и переводить на
Си.
\index{функция!getline@\texttt{getline}}%
Поэтому сначала напишем отдельную функцию \verb|getline| для получения
очередной строки. Мы попытаемся сделать эту функцию полезной и для других
применений. Как минимум \verb|getline| должна сигнализировать о возможном конце
файла, а ещё лучше, если она будет выдавать длину строки или нуль (в случае
исчерпания файла). Нуль годится для признака конца файла, поскольку не бывает
строк нулевой длины, -- даже строка, содержащая только одну литеру новая-строка
имеет длину 1.

Когда мы обнаружим строку более длинную, чем самая длинная из всех предыдущих,
то нам надо будет где-то её запомнить.
\index{функция!copy@\texttt{copy}}%
Здесь напрашивается вторая функция, \verb|copy|, которая умеет копировать новую
строку в надёжное место.

Наконец, нам необходима главная программа, которая бы управляла функциями
\verb|getline| и \verb|copy|. Вот как выглядит наша программа в целом:

\begin{LongCodePar}
#include <stdio.h>
#define MAXLINE 1000 /* макс-ный размер вводимой строки */
int getline(char line[], int maxline);
void copy(char to[], char from[]);
/* печать самой длинной строки */
main()
{
    int len;  /* длина текущей строки */
    int max; /* длина максимальной из просмотренных строк */
    char line[MAXLINE];       /* текущая строка */
    char longest[MAXLINE];    /* самая длинная строка */
    max = 0;
    while ((len = getline(line, MAXLINE)) > 0)
        if (len > max) {
            max = len;
            copy(longest, line);
        }
    if (max > 0)      /* была ли хоть одна строка? */
    printf("%s", longest);
    return 0;
}

/* getline: читает строку в s, возвращает длину */
int getline(char s[], int lim)
{
    int c, i;
    for (i=0; i<lim-1 && (c=getchar())!=EOF && c!='\n'; ++i)
        s[i] = c;
    if (c == '\n') {
        s[i] = c;
        ++i;
    }
    s[i] = '\0';
    return i;
}

/* copy: копирует из 'from' в 'to'; to достаточно большой */
void copy(char to[], char from[])
{
    int i;
    i = 0;
    while ((to[i] = from[i]) != '\0')
        ++i;
}
\end{LongCodePar}

Мы предполагаем, что функции \verb|getline| и \verb|copy|, описанные в начале
программы, находятся в том же файле, что и \verb|main|.

Функции \verb|main| и \verb|getline| взаимодействуют между собой через пару
аргументов и возвращаемое значение.
\index{прототип функции}%
\index{функция!прототип}%
В \verb|getline| аргументы определяются строкой

\begin{ShortCodePar}
int getline(char s[], int lim)
\end{ShortCodePar}

\noindent Как мы видим, её первый аргумент, \verb|s|, есть массив, а второй,
\verb|lim|, имеет целый тип. Задание размера массива в определении имеет целью
резервирование памяти. В самой \verb|getline| задавать длину массива \verb|s|
нет необходимости, так как его размер указан в \verb|main|.
\index{return-инструкция@\texttt{return}-инструкция}%
Чтобы вернуть значение вызывающей программе, \verb|getline| использует
\verb|return| точно так, как это делает функция \verb|power|.
\index{по умолчанию!тип функции}%
\index{функция!тип по умолчанию}%
В приведённой строке также сообщается, что \verb|getline| возвращают значение
типа \verb|int|, но так как при отсутствии указания о типе подразумевается
\verb|int|, то перед \verb|getline| слово \verb|int| можно опустить.

Одни функции возвращают результирующее значение, другие (как \verb|copy|) нужны
только для того, чтобы произвести какие-то действия, не выдавая никакого
значения.
\index{void@\texttt{void}!тип}%
На месте типа результата в \verb|copy| стоит \verb|void|. Это явное указание на
то, что никакого значения данная функция не возвращает.

Функция \verb|getline| в конец создаваемого ею массива помещает
\index{null-литера, 0@null-литера, \texttt{\textbackslash 0}}%
литеру \verb|'\0'| (\emph{null-литеру}, кодируемую нулевым байтом), чтобы
пометить конец стринга литер. То же соглашение относительно окончания
null-литерой соблюдается и в случае стринга литер типа

\begin{ShortCodePar}
"hello\n"
\end{ShortCodePar}

\noindent В данном случае для него формируется массив из литер этого стринга с
\verb|'\0'| в конце.

\begin{center}
\begin{tabular}{|c|c|c|c|c|c|c|}
\hline
\verb| h  |&\verb| e  |&\verb| l  |&\verb| l  |&\verb| o  |&
\verb|\n  |&\verb|\0  |\\
\hline
\end{tabular}
\end{center}

\noindent Спецификация \verb|%s|  в формате \verb|printf|  предполагает, что
соответствующий ей аргумент -- стринг, оформленный указанным выше образом.
Функция \verb|copy| в своей работе также опирается на тот факт, что читаемый ею
аргумент заканчивается литерой \verb|'\0'|‚ который она копирует наряду с
остальными литерами. (Все сказанное предполагает, что \verb|'\0'| не встречается
внутри обычного текста.)

Попутно стоит заметить, что даже на такой маленькой программе выявляются
некоторые конструктивные трудности. Например, что должна делать \verb|main|,
если встретится строка, превышающая допустимый размер? Функция \verb|getline|
работает надёжно: если массив полон, она прекращает пересылку, даже если литеры
новая-строка не обнаружила. Получив от \verb|getline| длину строки и увидев, что
она совпадает с \verb|MAXLINE|, главная программа могла бы <<отловить>> этот
особый случай и справиться с ним. В интересах краткости мы его здесь опускаем.

Пользователи \verb|getline| не могут заранее узнать, сколь длинными будут
вводимые строки, поэтому \verb|getline| делает проверки на переполнение. А вот
пользователям функции \verb|copy| размеры копируемых стрингов известны (или
они могут их узнать), поэтому дополнительный контроль здесь не нужен.

\paragraph{Упражнение 1.16.} Перепишите \verb|main| предыдущей программы так,
чтобы она могла печатать самую длинную строку без каких-либо ограничений на её
размер.

\paragraph{Упражнение 1.17.} Напишите программу печати всех вводимых строк,
содержащих более 80 литер.

\paragraph{Упражнение 1.18.} Напишите программу, которая будет в каждой вводимой
строке заменять подряд стоящие литеры пробелов и табуляций на один пробел и
удалять пустые строки.

\paragraph{Упражнение 1.19.} Напишите функцию \verb|reverse(s)|, размещающую
литеры в стринге \verb|s| в обратном порядке. Примените её при написании
программы, которая <<реверсирует>> каждую вводимую строку.

\section{Внешние переменные и область действия}

\index{декларация!внешней переменной}%
Переменные \verb|line|, \verb|longest| и т.д. принадлежат только функции
\verb|main|, или, как говорят, локализованы в ней. Поскольку они определены
внутри \verb|main|, никакие другие функции прямо к ним обращаться не могут. То
же верно и применительно к переменным других функций; например, \verb|i| в
\verb|getline| не имеет никакого отношения к \verb|i| в \verb|copy|.
\index{инициализация!переменных!автоматических}%
\index{память!автоматическая}%
\index{переменная!автоматическая}%
Каждая локальная переменная функции возникает только в момент обращения к этой
функции и исчезает после выхода из неё. Вот почему такие переменные, следуя
терминологии других языков, называют \emph{автоматическими}. (В
гл.~\ref{chapt:functions_and_program_structure} обсуждается класс памяти
\verb|static|, который позволяет локальным переменным сохранять свои значения в
промежутках между вызовами.)

Так как автоматические переменные образуются и исчезают одновременно с входом в
функцию и выходом из неё, они не сохраняют своих значений от вызова к вызову и
должны устанавливаться заново при каждом новом обращении к функции. Если этого
не делать, они будут содержать <<мусор>>.

\index{переменная!внешняя}%
В качестве альтернативы автоматическим переменным можно определить
\emph{внешние} переменные, к которым разрешается обращаться по их именам из
любой функции. (Этот механизм аналогичен области \verb|COMMON| в Фортране и
определениям переменных в самом внешнем блоке в Паскале.) Так как внешние
переменные доступны повсеместно, их можно использовать вместо аргументов для
связи между функциями по данным. Кроме того, поскольку внешние переменные
существуют постоянно, а не возникают и исчезают на период активизации функции,
свои значения они сохраняют и после возврата из функций, их установивших.

\index{определение!внешней переменной}%
\index{спецификатор!extern@\texttt{extern}}%
Внешняя переменная должна быть \emph{определена}, причём только один раз, вне
текста любой функции; в этом случае ей будет выделена память. Она должна быть
\emph{описана} (\emph{декларирована}) во всех функциях, которые хотят ею
пользоваться. Описание содержит сведения о типе переменной.
\index{extern@\texttt{extern}}%
Описание может быть явным, в виде инструкции \verb|extern|, или неявным, когда
нужная информация получается из контекста.
\index{программа!печати!самой длинной строки}%
Чтобы конкретизировать сказанное перепишем программу печати самой длинной строки
с использованием \verb|line|, \verb|longest| и \verb|max| в качестве внешних
переменных.
\index{функция!copy@\texttt{copy}}%
\index{функция!getline@\texttt{getline}}%
Это потребует изменений в вызовах, описаниях и телах всех трёх функций.

\begin{LongCodePar}
#include <stdio.h>
#define MAXLINE 1000 /* макс-ный размер вводимой строки */
int max;  /* длина максимальной из просмотренных строк */
char line[MAXLINE];            /* текущая строка */
char longest[MAXLINE];         /* самая длинная строка */
int getline(void);
void copy(void);
/* печать самой длинной строки */
main()
{
    int len;
    extern int max;
    extern char longest[];
    max = 0;
    while ((len = getline()) > 0)
        if (len > max) {
            max = len;
            copy();
        }
    if (max > 0)      /* была хотя бы одна строка */
        printf("%s", longest);
    return 0;
}

/* getline: специализированная версия */
int getline(void)
{
    int c, i;
    extern char line[];
    for (i = 0; i < MAXLINE-1
        &&  (c=getchar()) != EOF && c != '\n'; ++i)
            line[i] = c;
    if (c == '\n') {
        line[i] = c;
        ++i;
    }
    line[i] = '\0';
    return i;
}

/* copy: специализированная версия */
void copy(void)
{
    int i;
    extern char line[], longest[];
    i = 0;
    while ((longest[i] = line[i]) != '\0')
        ++i;
}
\end{LongCodePar}

Внешние переменные для \verb|main|, \verb|getline| и \verb|copy| определяются в
начале нашего примера, где им присваивается тип и выделяется память. Определения
внешних переменных синтаксически ничем не отличаются от определения локальных
переменных, но поскольку они расположены вне функций, эти переменные считаются
внешними. Чтобы функция могла пользоваться внешней переменной, ей нужно прежде
всего сообщить имя соответствующей переменной.
\index{спецификатор!extern@\texttt{extern}}%
\index{extern@\texttt{extern}}%
Это можно сделать, например, задав описание \verb|extern|, которое по виду
отличается от определения внешней переменной только тем, что оно начинается с
ключевого слова \verb|extern|.

\index{спецификатор!extern@\texttt{extern}}%
\index{extern@\texttt{extern}}%
В некоторых случаях описание \verb|extern| можно опустить. Если определение
внешней переменной в исходном файле расположено выше функции, где она
используется, то в описании \verb|extern| нет необходимости. Описания
\verb|extern|, таким образом, в \verb|main|, \verb|getline| и \verb|copy|
избыточны. Обычно определения внешних переменных располагают в начале исходного
файла, и все описания \verb|extern| для них опускают.

\index{файл!головной}%
Если же программа расположена в нескольких исходных файлах и внешняя переменная
определена в \textit{файле1}, а используется в \textit{файле2} и
\textit{файлеЗ}, то \verb|extern|-описания в \textit{файле2} и \textit{файлеЗ}
обязательны, поскольку необходимо указать, что во всех трёх файлах функции
ссылаются на одну и ту же внешнюю переменную.
\index{include@\texttt{{\#}include}}%
На практике обычно удобно собрать
все описания внешних переменных и функций в отдельный файл, называемый
\emph{головным} (\emph{header-файлом}), и помещать его с помощью \verb|#include|
в начало каждого исходного файла.
\index{файл!суффикс имени \texttt{.h}}%
\index{h (суффикс имени файла)@\texttt{.h} (суффикс имени файла)}%
Суффикс \verb|.h| по общей договорённости используется для имён header-файлов. В
головных файлах, в \verb|<stdio.h>| в частности, описываются также функции
стандартной библиотеки. Более подробно о головных файлах говорится в
гл.~\ref{chapt:functions_and_program_structure}, а применительно к стандартной
библиотеке -- в гл.~\ref{chapt:input-output} и приложении~\ref{apx:stdlib}.

Так как специализированные версии \verb|getline| и \verb|copy| не имеют
аргументов, на первый взгляд кажется, что логично их прототипы задать в виде
\verb|getline()| и \verb|copy()|.
\index{декларация!функции!старым способом}%
\index{функция!в старом стиле}%
Но из соображений совместимости со старыми Си-программами стандарт рассматривает
пустой список как сигнал к тому, чтобы выключить все проверки на соответствие
аргументов.
\index{void@\texttt{void}!список аргументов}%
Поэтому, когда нужно сохранить контроль и явно указать отсутствие аргументов,
следует пользоваться словом \verb|void|. Мы вернёмся к этой проблеме
в гл.~\ref{chapt:functions_and_program_structure}.

\index{модульность}%
\index{декларация!а не определение}%
Заметим, что по отношению к внешним переменным в этом разделе мы очень аккуратно
используем понятия \emph{определение} и \emph{описание}. <<Определение>>
располагается в месте, где переменная создаётся и ей отводится память;
<<описание>> помещается там, где фиксируется природа переменной, но никакой
памяти для неё не отводится.

Следует отметить тенденцию все переменные делать внешними. Дело в том, что, как
может показаться на первый взгляд, это приводит к упрощению связей -- ведь
списки аргументов становятся короче, а переменные везде доступны, где они нужны;
правда, они оказываются доступными и там, где они не нужны. Однако чрезмерный
акцент на внешние переменные чреват большими опасностями -- он приводит к
программам, в которых связи по данным не очевидны, поскольку переменные могут
неожиданным и даже таинственным способом изменяться. Кроме того, такая программа
с трудом поддаётся модификациям. Вторая версия программы поиска самой длинной
строки хуже, чем первая, частично по этим причинам, а частично из-за нарушения
общности двух полезных функций, вызванного тем, что в них вписаны имена
конкретных переменных, с которыми они оперируют.

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

\paragraph{Упражнение 1.20.}\phantomsection\label{parag:exc1.20} Напишите
программу \verb|detab|, заменяющую литеры табуляции вводимого текста нужным
числом пробелов (до следующего <<стопа>> табуляции). Предполагается, что
<<стопы>> табуляции расставлены на фиксированном расстоянии друг от друга,
скажем, через \verb|n| позиций. Как лучше задавать \verb|n| -- в виде значения
переменной или в виде именованной константы?

\paragraph{Упражнение 1.21.}\phantomsection\label{parag:exc1.21} Напишите
программу \verb|entab|, заменяющую цепочки пробелов минимальным числом литер
табуляций и пробелов таким образом, чтобы вид напечатанного текста не изменился.
Используйте те же <<стопы>> табуляции, что и в \verb|detab|. В случае, когда для
выхода на очередной <<стоп>> годится один пробел, что лучше -- пробел или литера
табуляции?

\paragraph{Упражнение 1.22.} Напишите программу, печатающую литеры входного
потока так, чтобы строки текста не выходили правее $n$-й позиции. Это значит,
что каждая строка, длина которой превышает $n$, должна печататься с переносом на
следующие строки. Место переноса следует <<искать>> после последней непробельной
литеры, расположенной левее \verb|n|-й позиции. Позаботьтесь о том, чтобы ваша
программа вела себя разумно в случае очень длинных строк, а также, когда до
\verb|n|-й позиции не встречается ни одной литеры пробела или табуляции.
%%
%% разные шрифты для n в предыдущем абзаце это особенность оригинала
%%

\paragraph{Упражнение 1.23.} Напишите программу, убирающую все комментарии из
любой Си-программы. Не забудьте должным образом обработать стринги литер и
стринговые константы. Комментарии в Си не могут быть вложены друг в друга.

\paragraph{Упражнение 1.24.} Напишите программу, проверяющую Си-программы на
элементарные синтаксические ошибки типа несбалансированности скобок всех видов.
Не забудьте о кавычках (одиночных и двойных), эскейп-последовательностях
(\verb|\|\ldots) и комментариях. (Это -- сложная программа, если её писать для
общего случая.)
