
\chapter{Управление}
\label{chapt:control_flow}

Порядок, в котором выполняются вычисления, определяется инструкциями управления.
Мы уже встречались с наиболее распространёнными управляющими конструкциями
такого рода в предыдущих примерах; здесь мы завершим их список и более точно
определим рассмотренные ранее.

\section{Инструкции и блоки}

\index{выражение-инструкция}%
\index{точка с запятой}%
Выражение, скажем \verb|x = 0|, или \verb|i++|, или \verb|printf(...)|,
становится инструкцией, если в конце его поставить точку с запятой, как,
например, в записи

\begin{ShortCodePar}
x = 0;
i++;
printf(...);
\end{ShortCodePar}

\noindent В Си точка с запятой является заключающей литерой инструкции, а не
разделителем, как в языке Паскаль.

\index{блок}%
\index{инструкция!окончание}%
\index{составная инструкция}%
\index{инструкция!составная}%
\index{фигурные скобки}%
Фигурные скобки \verb|{| и \verb|}| используются для объединения описаний и
инструкций в \emph{составную инструкцию}, или \emph{блок}, чтобы с точки зрения
синтаксиса эта новая конструкция воспринималась как одна инструкция. Фигурные
скобки, обрамляющие группу инструкций, образующих тело функции, -- это один
пример; второй пример -- это скобки, объединяющие инструкции, помещённые после
\verb|if|, \verb|else|, \verb|while| или \verb|for|.
\index{блок!структура}%
(Переменные могут быть описаны внутри \emph{любого} блока, об этом разговор
пойдёт в гл.~\ref{chapt:functions_and_program_structure}) После правой
закрывающей фигурной скобки в конце блока точка с запятой не ставится.

\section{Конструкция \texorpdfstring{\protect\Verb|if-else|}{if-else}}

\index{if-else@\texttt{if-else}!инструкция}%
Инструкция \verb|if-else| используется для принятия решения. Вот её синтаксис:

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

\noindent причём \verb|else|-часть может быть, а может и отсутствовать. Сначала
вычисляется \textit{выражение}, и, если оно истинно (т.е. отлично от нуля),
выполняется \textit{инструкция}$_{1}$. Если \textit{выражение} ложно (т.е. его
значение равно нулю) и существует \verb|else|-часть, то выполняется
\textit{инструкция}$_{2}$.

\index{нуль, опущенная проверка на него}%
Так как \verb|if| просто проверяет числовое значение выражения, условие иногда
можно записывать в сокращённом виде. Так, запись

\begin{ShortCodeParWithCC}{\\\{\}}
if (\textit{выражение})
\end{ShortCodeParWithCC}

\noindent короче, чем

\begin{ShortCodeParWithCC}{\\\{\}}
if (\textit{выражение} != 0)
\end{ShortCodeParWithCC}

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

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

\begin{ShortCodePar}
if (n > 0)
    if (a > b)
        z = a;
    else
        z = b;
\end{ShortCodePar}

\noindent \verb|else| относится к внутреннему \verb|if|, что мы и показали с
помощью отступов. Если нам требуется иная интерпретация, необходимо должным
образом расставить фигурные скобки:

\begin{ShortCodePar}
if (n > 0) {
    if (a > b)
        z = a;
}
else
    z = b;
\end{ShortCodePar}

Ниже приводится пример ситуации, когда неоднозначность особенно опасна:

\begin{LongCodePar}
if (n >= 0)
    for (i = 0; i < n; i++)
        if (s[i] > 0) {
            printf("...");
            return i;
        }
else        /* НЕВЕРНО */
    printf("ошибка -- отрицательное n\n");
\end{LongCodePar}

\noindent%
\index{отступы в тексте программы}%
С помощью отступов мы недвусмысленно показали, что нам нужно, однако компилятор
не воспримет эту информацию и отнесёт \verb|else| к внутреннему \verb|if|.
Такого рода ошибки особенно тяжело искать. Здесь уместен следующий совет:
вложенные \verb|if| обрамляйте фигурными скобками.

\index{точка с запятой}%
Кстати, обратите внимание на точку с запятой после \verb|z = a| в 

\begin{ShortCodePar}
if (a > b)
    z = a;
else
    z = b;
\end{ShortCodePar}

\noindent Здесь она обязательна, поскольку по правилам грамматики за \verb|if|
должна следовать инструкция, а
\index{выражение-инструкция}%
выражение-инструкция типа \verb|z = a;| всегда заканчивается точкой с запятой.

\section{Конструкция \texorpdfstring{\protect\Verb|else-if|}{else-if}}
\label{sec:else_if}

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

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

\noindent встречается так часто, что о ней стоит поговорить особо. Приведённая
последовательность инструкций \verb|if| -- самый общий способ описания
многоступенчатого принятия решения. \textit{Выражения} вычисляются по порядку;
как только встречается \textit{выражение} со значением <<истина>>, выполняется
соответствующая ему \textit{инструкция}; на этом последовательность проверок
завершается. Здесь под словом \textit{инструкция} имеется в виду либо одна
инструкция, либо группа инструкций в фигурных скобках.

Последняя \verb|else|-часть срабатывает, если все предыдущие условия не
выполняются. Иногда в последней части не требуется производить никаких действий,
в этом случае фрагмент

\begin{ShortCodeParWithCC}{\\\{\}}
else
    \textit{инструкция}
\end{ShortCodeParWithCC}

\noindent можно опустить или использовать для фиксации ошибочной
(<<невозможной>>) ситуации.

В качестве иллюстрации трёхпутевого ветвления рассмотрим функцию бинарного
поиска значения \verb|x| в массиве \verb|v|. Предполагается, что элементы
\verb|v| упорядочены по возрастанию. Функция выдаёт положение (число в пределах
от $0$ до $n-1$) \verb|x| в \verb|v|, если оно там встречается, и $-1$, если его
нет.

При бинарном поиске значение \verb|x| сначала сравнивается с элементом,
занимающим серединное положение в массиве \verb|v|. Если \verb|x| меньше, чем
это значение, то областью поиска становится <<верхняя>> половина массива
\verb|v|, в противном случае -- <<нижняя>>. В любом случае следующий шаг --
это сравнение с серединным элементом отобранной половины. Процесс
<<уполовинивания>> диапазона продолжается до тех пор, пока либо будет найдено
значение, либо станет пустым диапазон.
\index{функция!binsearch@\texttt{binsearch}}%
Запишем функцию бинарного поиска:

\begin{LongCodePar}
/* binsearch: найти x в v[0] <= v[1] <= ... <= v[n-1] */
int binsearch(int x, int v[], int n)
{
    int low, high, mid;
    low = 0;
    high = n - 1;
    while (low <= high) {
        mid = (low+high) / 2;
        if (x < v[mid])
            high = mid - 1;
        else if (x > v[mid])
            low = mid + 1;
        else     /* x найден */
            return mid;
    }
    return -1;   /* x не найден */
} 
\end{LongCodePar}

\noindent Основное действие, выполняемое на каждом шаге поиска, -- сравнение
значения \verb|x| (меньше, больше или равно) с элементом \verb|v[mid];| это
сравнение естественно поручить конструкции \verb|else-if|.

\paragraph{Упражнение 3.1.} В нашей программе бинарного поиска внутри цикла
осуществляются две проверки, хотя могла быть только одна (при увеличении числа
проверок вне цикла). Напишите программу, предусмотрев в ней одну проверку внутри
цикла. Оцените разницу во времени счёта.

\section{Переключатель}
\label{sec:switch}

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

\begin{ShortCodeParWithCC}{\\\[\]}
switch (\textit[выражение]) {
    case \textit[конст-выр]: \textit[инструкции]
    case \textit[конст-выр]: \textit[инструкции]
    default: \textit[инструкции]
}
\end{ShortCodeParWithCC}

\noindent%
\index{метка!case@\texttt{case}}%
\index{case-метка@\texttt{case}-метка}%
Каждая ветвь \verb|case|
помечена одной или несколькими целочисленными константами или же
\index{выражение!константное}%
\index{константное выражение}%
константными выражениями. Вычисления начинаются с той ветви \verb|case|, в
которой константа совпадает со значением выражения. Константы всех ветвей
\verb|case| должны отличаться друг от друга.
\index{метка!default@\texttt{default}}%
\index{default@\texttt{default}}%
Если выяснилось, что ни одна из констант не подходит, то выполняется ветвь,
помеченная словом \verb|default|, если таковая имеется, в противном случае
ничего не делается. Ветви \verb|case| и \verb|default| можно располагать в любом
порядке.

\index{программа!подсчёта!литер}%
\index{программа!подсчёта!пробельных литер}%
В гл.~\ref{chapt:tutorial_intro} мы написали программу, подсчитывающую число
вхождений в текст каждой цифры, пробельных литер (пробелов, табуляций и
новых-строк) и всех остальных литер. В ней мы использовали последовательность
\verb|if...else if...else|. Теперь приведём вариант этой программы с
переключателем:

\begin{LongCodePar}
#include <stdio.h>
main()  /* подсчёт цифр, пробелов и прочих литер */
{
    int c, i, nwhite, nother, ndigit[10];
    nwhite = nother = 0;
    for (i = 0; i < 10; i++)
        ndigit[i] = 0;
    while ((c = getchar()) != EOF) {
        switch (c) {
        case '0': case '1': case '2': case '3': case '4':
        case '5': case '6': case '7': case '8': case '9':
            ndigit[c-'0']++;
            break;
        case ' ': case '\n': case '\t':
            nwhite++;
            break;
        default:
            nother++;
            break;
        }
    }
    printf("цифры =");
    for (i = 0; i < 10; i++)
        printf(" %d", ndigit[i]);
    printf(", пробелы = %d, прочие = %d\n",
        nwhite, nother);
    return 0;
} 
\end{LongCodePar}

\index{break@\texttt{break}}%
Инструкция \verb|break| вызывает немедленный выход из переключателя. Поскольку
выбор ветви \verb|case| реализуется как переход на метку, то после выполнения
одной ветви \verb|case|, если ничего не предпринять, программа \emph{провалится
вниз} на следующую ветвь. Инструкции \verb|break| и \verb|return| -- наиболее
распространённые средства выхода из переключателя. Инструкция \verb|break|
используется также для принудительного выхода из циклов \verb|while|, \verb|for|
и \verb|do-while| (мы ещё поговорим об этом чуть позже).

<<Сквозное>> выполнение ветвей \verb|case| вызывает смешанные чувства. С одной
стороны, это хорошо, поскольку позволяет несколько ветвей \verb|case| объединить
в одну, как мы и поступили с цифрами в нашем примере. Но с другой -- это
означает, что в конце почти каждой ветви придётся ставить \verb|break|, чтобы
избежать перехода к следующей. Последовательный проход по ветвям -- вещь
ненадёжная, он чреват ошибками, особенно при изменении программы. За исключением
случая с несколькими метками для одного вычисления, старайтесь по возможности
редко пользоваться сквозным проходом, но если уж вы его применяете, обязательно
комментируйте эти особые места.

Добрый вам совет: даже в конце последней ветви (после \verb|default| в нашем
примере) помещайте инструкцию \verb|break|, хотя с точки зрения логики в ней нет
необходимости. Но эта маленькая предосторожность спасёт вас, когда однажды вам
потребуется добавить в конец ещё одну ветвь \verb|case|.

\paragraph{Упражнение 3.2.} Напишите функцию \verb|escape(s,t)|, которая при
копировании текста из \verb|t| в \verb|s| преобразует литеры типа новая-строка и
табуляция в <<видимые последовательности литер>> (типа \verb|\n| и \verb|\t|).
Используйте инструкцию \verb|switch|. Напишите функцию, выполняющую обратное
преобразование эскейп-последовательностей в настоящие литеры.

\section{Циклы
\texorpdfstring{\protect\Verb|while|}{while} и
\texorpdfstring{\protect\Verb|for|}{for}}

\index{while@\texttt{while}!инструкция}%
Мы уже встречались с циклами \verb|while| и \verb|for|. В цикле

\begin{ShortCodeParWithCC}{\\\{\}}
while (\textit{выражение})
    \textit{инструкция}
\end{ShortCodeParWithCC}

\noindent вычисляется \textit{выражение}. Если его значение отлично от нуля, то
выполняется \textit{инструкция}, и вычисление выражения повторяется. Этот цикл
продолжается до тех пор, пока выражение не станет равным нулю, после чего
вычисления возобновятся с точки, расположенной сразу за \textit{инструкцией}.

\begin{samepage}
\index{for@\texttt{for}!вместо \texttt{while}}%
\index{for@\texttt{for}!инструкция}%
\index{while@\texttt{while}!вместо \texttt{for}}%
Инструкция \verb|for|

\begin{ShortCodeParWithCC}{\\\{\}}
for (\textit{выр}\textsubscript{1}; \textit{выр}\textsubscript{2};\
\textit{выр}\textsubscript{3})
    \textit{инструкция}
\end{ShortCodeParWithCC}

\noindent эквивалентна конструкции

\begin{ShortCodeParWithCC}{\\\[\]}
\textit[выр]\textsubscript[1];
while (\textit[выр]\textsubscript[2]) {
    \textit[инструкция]
    \textit[выр]\textsubscript[3];
}
\end{ShortCodeParWithCC}

\noindent если не считать отличий в поведении инструкции \verb|continue|, речь о
которой пойдёт в разд.~\ref{sec:inst_break_and_continue}.
\end{samepage}

С точки зрения грамматики три компоненты цикла \verb|for| представляют собой
произвольные выражения, но чаще \textit{выр}$_1$ и \textit{выр}$_3$ -- это
присваивания или вызовы функций, а \textit{выр}$_2$ -- выражение отношения.
Любое из этих трёх выражений может отсутствовать, но точку с запятой опускать
нельзя. При отсутствии \textit{выр}$_1$ или \textit{выр}$_3$ считается, что их
просто нет в конструкции цикла; при отсутствии \textit{выр}$_2$ полагается, что
его значение как бы всегда истинно.
\index{бесконечный цикл \texttt{for(;;)}}%
\index{for(;;) бесконечный цикл@\texttt{for(;;)} бесконечный цикл}%
Например,

\begin{ShortCodePar}
for (;;) {
    ...
}
\end{ShortCodePar}

\noindent есть <<бесконечный>> цикл, выполнение которого, вероятно, прерывается
каким-то другим способом, например с помощью инструкций \verb|break|, или
\verb|return|.

Какой цикл выбрать: \verb|while| или \verb|for| -- это дело вкуса. Так, в

\begin{ShortCodePar}
while ((c=getchar()) == ' ' || c == '\n' || c == '\t')
    ;   /* обойти пробельные литеры */ 
\end{ShortCodePar}

\noindent нет ни инициализации, ни пересчёта параметра, поэтому здесь больше
подходит \verb|while|.

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

\begin{ShortCodePar}
for (i = 0; i < n; i++)
    ...
\end{ShortCodePar}

\noindent Это похоже на \verb|DO|-циклы в Фортране и \verb|for|-циклы в Паскале.
Сходство, однако, не очень точное, так как в Си индекс и его предельное значение
могут изменяться внутри цикла, и значение индекса \verb|i| после выхода из цикла
всегда определено.

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

\index{функция!atoi@\texttt{atoi}}%
В качестве более внушительного примера приведём другую версию программы
\verb|atoi|, выполняющей преобразование стринга в его числовой эквивалент. Это
более общая версия по сравнению с рассмотренной в
гл.~\ref{chapt:types-operators-expressions}, в том смысле, что она игнорирует
левые пробельные литеры (если они есть) и должным образом реагирует на знаки
\verb|+| и \verb|-|, которые могут стоять перед цепочкой цифр. (В
гл.~\ref{chapt:functions_and_program_structure} будет рассмотрен вариант
\verb|atof|, который осуществляет подобное преобразование для чисел с плавающей
точкой.)

Структура программы отражает вид вводимой информации:

\begin{ShortCodeParWithCC}{\\\{\}}
\textit{проигнорировать пробельные литеры, если они есть}
\textit{получить знак, если он есть}
\textit{взять целую часть и преобразовать её}
\end{ShortCodeParWithCC}

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

\begin{LongCodePar}
#include <ctype.h>
/* atoi: преобразование s в целое число; версия 2 */
int atoi(char s[])
{
    int i, n, sign;
    for (i = 0; isspace(s[i]); i++)
        ;           /* проигнорировать пробельные литеры */
    sign = (s[i] == '-') ? -1 : 1;
    if (s[i] == '+' || s[i] == '-') /* пропуск знака */
        i++;
    for (n = 0; isdigit(s[i]); i++)
        n = 10 * n + (s[i] - '0');
    return sign * n;
}
\end{LongCodePar}

\noindent Заметим, что в стандартной библиотеке имеется более совершенная
функция преобразования стринга в длинное целое -- \verb|strtol| (см.
разд.~\ref{apx:sect:general_puporse_functions_stdlib}
приложения~\ref{apx:stdlib}).

Преимущества, которые даёт централизация управления циклом, становятся ещё более
очевидными, когда несколько циклов вложены друг в друга.
\index{программа!сортировки}%
\index{сортировка!численная}%
\index{функция!shellsort@\texttt{shellsort}}%
\index{числа!сортировка}%
Проиллюстрируем их на примере сортировки массива целых чисел методом
\index{Шелл Д. Л.}%
Шелла, предложенным им в 1959 г. Основная идея этого алгоритма в том, что на
ранних стадиях сравниваются далеко отстоящие друг от друга, а не соседние
элементы, как в обычных перестановочных сортировках. Это приводит к быстрому
устранению массовой неупорядоченности, благодаря чему на более поздней стадии
остаётся меньше работы. Интервал между сравниваемыми элементами постепенно
уменьшается до единицы, и в этот момент сортировка сводится к обычным
перестановкам соседних элементов. Программа \verb|shellsort| имеет следующий
вид:

\begin{LongCodePar}
/* shellsort: сортируются v[0]...v[n-1] в возр.порядке */ 
void shellsort(int v[], int n)
{
    int gap, i, j, temp;
    for (gap = n/2; gap > 0; gap /= 2)
        for (i = gap; i < n; i++)
            for (j=i-gap; j>=0 && v[j]>v[j+gap]; j-=gap) {
                temp = v[j];
                v[j] = v[j+gap];
                v[j+gap] = temp;
            }
}
\end{LongCodePar}

Здесь использованы три вложенных друг в друга цикла. Внешний управляет
интервалом \verb|gap| между сравниваемыми элементами, сокращая его с помощью
деления пополам от \verb|n/2| до нуля. Средний цикл перебирает элементы.
Внутренний -- сравнивает каждую пару элементов, отстоящих друг от друга на
расстоянии \verb|gap|, и осуществляет перестановку элементов в неупорядоченных
парах. Так как \verb|gap| обязательно будет сведён к единице, все элементы в
конечном счёте будут упорядочены. Обратите внимание на то, что универсальность
цикла \verb|for| позволяет сделать внешний цикл по форме похожим на другие, хотя
он и не является циклом типа арифметической прогрессии.

\index{оператор!запятая \texttt{,}}%
Последний оператор из числа операторов Си <<\verb|,|>> чаще всего используют в
инструкции \verb|for|. Пара выражений, разделённых запятой,
\index{вычисление, порядок}%
\index{порядок!вычислений}%
вычисляется слева направо. Типом и значением результата являются тип и значение
правого выражения, что позволяет в инструкции \verb|for| в каждой из трёх
компонент иметь по нескольку выражений, например вести два индекса параллельно.
\index{функция!reverse@\texttt{reverse}}%
Продемонстрируем это на примере функции \verb|reverse(s)|, которая
<<переворачивает>> стринг \verb|s|, оставляя результат в том же стринге
\verb|s|:

\begin{LongCodePar}
#include <string.h>
/* reverse: переворачивает стринг s (результат в s) */
void reverse(char s[])
{
    int c, i, j;
    for (i = 0, j = strlen(s)-1; i < j; i++, j--) {
        c = s[i];
        s[i] = s[j];
        s[j] = c;
    }
}
\end{LongCodePar}

\noindent Запятые, разделяющие аргументы функции, переменные в описаниях и т.д.
\emph{не} являются операторами-запятыми и не обеспечивают вычислений слева
направо.

Запятыми как операторами следует пользоваться умеренно. Более всего они уместны
в конструкциях, которые тесно связаны друг с другом (как в \verb|for|-цикле
программы \verb|reverse|), а также в макросах, в которых многоступенчатые
вычисления должны быть выражены одним выражением. Запятой-оператором в программе
\verb|reverse| можно было бы воспользоваться и при обмене литерами в проверяемых
парах элементов стринга, мысля этот обмен как одну отдельную операцию:

\begin{ShortCodePar}
for (i = 0, j = strlen(s)-1; i < j; i++, j--)
    c = s[i], s[i] = s[j], s[j] = c; 
\end{ShortCodePar}

\paragraph{Упражнение 3.3.} Напишите функцию \verb|expand(s1,s2)|,
восстанавливающую сокращённую запись типа \verb|a-z| в стринге \verb|s1| до
эквивалентной полной записи \verb|abc...xyz| в \verb|s2|. В \verb|s1|
допускаются буквы (прописные и строчные) и цифры. Следует уметь справляться со
случаями типа \verb|a-b-c|, \verb|a-z0-9| и \verb|-a-b|. Считайте знак
\verb|-| в начале или в конце \verb|s1| обычной литерой минус.

\section{Цикл \texorpdfstring{\protect\Verb|do-while|}{do-while}}
\label{sec:do_while}

Как мы говорили в гл.~\ref{chapt:tutorial_intro}, в циклах \verb|while| и
\verb|for| проверка условия окончания цикла выполняется наверху.
\index{do-инструкция@\texttt{do}-инструкция}%
В Си имеется ещё один вид цикла, \verb|do-while|, в котором эта проверка в
отличие от \verb|while| и \verb|for| делается внизу \emph{после} каждого
прохождения тела цикла, т.е. после того, как тело выполнится по крайней мере 
один раз.

Цикл \verb|do-while| имеет следующий синтаксис:

\begin{ShortCodeParWithCC}{\\\{\}}
do
    \textit{инструкция}
while (\textit{выражение});
\end{ShortCodeParWithCC}

\noindent%
\index{вычисление, порядок}%
Сначала выполняется \textit{инструкция}, затем вычисляется \textit{выражение}.
Если оно истинно, то \textit{инструкция} выполняется снова и т.д. Когда
выражение становится ложным, цикл заканчивает работу. Цикл \verb|do-while|
эквивалентен циклу \verb|repeat-until| в Паскале с той лишь разницей, что в
первом случае указывается условие продолжения цикла, а во втором -- условие его
окончания.

Опыт показывает, что цикл \verb|do-while| гораздо реже используется, чем
\verb|while| и \verb|for|.
\index{функция!itoa@\texttt{itoa}}%
Тем не менее потребность в нём время от времени возникает, как, например, в
функции \verb|itoa| (обратной по отношению к \verb|atoi|), преобразующей число в
стринг литер. Выполнить такое преобразование оказалось несколько более сложным
делом, чем ожидалось, поскольку простые алгоритмы генерируют цифры в обратном
порядке. Мы остановились на варианте, в котором сначала формируется цепочка
цифр, а затем она реверсируется.

\begin{LongCodePar}
/* itoa: преобразование n в стринг s */ 
void itoa(int n, char s[])
{
    int i, sign;
    if ((sign = n) < 0) /* сохраняем знак */ 
        n = -n;         /* делаем n положительным */ 
    i = 0;
    do {    /* генерируем цифры в обратном порядке */
        s[i++] = n % 10 + '0';  /* следующая цифра */
    } while ((n /= 10) > 0);    /* исключить её */
    if (sign < 0)
        s[i++] = '-';
    s[i] = '\0';
    reverse(s);
}    
\end{LongCodePar}

\noindent Конструкция \verb|do-while| здесь необходима или по крайней мере
удобна, поскольку в \verb|s| посылается хотя бы одна литера, даже если \verb|n|
равно нулю.
\index{программа!читаемость}%
В теле цикла одну инструкцию мы выделили фигурными скобками (хотя они и
избыточны), чтобы неискушённый читатель не принял по ошибке слово \verb|while|
за \emph{начало} цикла \verb|while|.

\paragraph{Упражнение 3.4.} При условии, что для представления чисел
используется дополнительный код, наша версия \verb|itoa| не справляется с самым
большим по модулю отрицательным числом, значение которого равняется $-2^{n-1}$
(где $n$ -- размер слова). Объясните, чем это вызвано. Модифицируйте программу
таким образом, чтобы она давала правильное значение указанного числа независимо
от машины, на которой выполняется.

\paragraph{Упражнение 3.5.} Напишите функцию \verb|itob(n,s,b)|, которая
переводит целое \verb|n| в стринг \verb|s|, представляющий число по основанию
\verb|b|. В частности, \verb|itob(n,s,16)| помещает в \verb|s| текст числа
\verb|n| в шестнадцатиричном виде.

\paragraph{Упражнение 3.6.} Напишите версию \verb|itoa| с дополнительным третьим
аргументом, задающим минимальную ширину поля. При необходимости преобразованное
число слева должно дополняться пробелами.

\section{Инструкции \texorpdfstring{\protect\Verb|break|}{break} и
\texorpdfstring{\protect\Verb|continue|}{continue}}
\label{sec:inst_break_and_continue}

\index{break@\texttt{break}}%
\index{continue@\texttt{continue}}%
Иногда бывает удобно выйти из цикла не по результату проверки, осуществляемой в
начале или в конце цикла, а каким-то другим способом. Такую возможность для
циклов \verb|for|, \verb|while| и \verb|do-while|, а также для переключателя
\verb|switch| предоставляет инструкция \verb|break|. Эта инструкция вызывает
немедленный выход из самого внутреннего из объемлющих её циклов или
переключателей.

\index{функция!trim@\texttt{trim}}%
Следующая функция, \verb|trim|, удаляет из стринга хвостовые литеры пробелов,
табуляций и новых-строк; \verb|break| используется в ней для выхода из цикла по
первой обнаруженной справа литере, отличной от названных.

\begin{LongCodePar}
/* trim: удаляет хвостовые пробелы, таб. и нов.-строки */
int trim(char[])
{
    int n;
    for (n = strlen(s)-1; n >= 0; n--)
        if (s[n] != ' ' && s[n] != '\t' && s[n] != '\n')
            break;
    s[n+1] = '\0';
    return n;
}
\end{LongCodePar}

С помощью функции \verb|strlen| можно получить длину стринга. Цикл \verb|for|
просматривает его в обратном порядке, начиная с конца, до тех пор, пока не
встретится литера, отличная от пробела, табуляции и новой-строки. Цикл
прерывается, как только такая литера обнаружится или \verb|n| станет
отрицательным (т.е. вся строка будет просмотрена). Убедитесь, что функция ведёт
себя правильно и в случаях, когда стринг пуст или состоит только из пробельных
литер.

Инструкция \verb|continue| в чём-то похожа на \verb|break|, но применяется
гораздо реже. Она вынуждает ближайший объемлющий её цикл (\verb|for|,
\verb|while| или \verb|do-while|) начать следующий шаг итерации. Для
\verb|while| и \verb|do-while| это означает немедленный переход к проверке
условия, а для \verb|for| -- к приращению шага. Инструкцию \verb|continue|
можно применять только к циклам, но не к \verb|switch|. Будучи помещённой внутрь
\verb|switch|, расположенного в цикле, она вызовет переход к следующей итерации
этого цикла.

Вот фрагмент программы, обрабатывающий только неотрицательные элементы массива
\verb|a| (отрицательные -- пропускаются).

\begin{ShortCodePar}
for (i = 0; i < n; i++) {
    if (a[i] < 0)  /* пропуск отрицательных элементов */
        continue;
    ... /* обработка положительных элементов */
}
\end{ShortCodePar}

\noindent К инструкции \verb|continue| часто прибегают в тех случаях, когда
оставшаяся часть цикла сложна, а замена условия в нём на противоположное и
введение еще одного уровня приводит к слишком большому числу уровней
вложенности.

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

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

Однако существуют случаи, в которых \verb|goto| может пригодиться. Наиболее
типична ситуация, когда необходимо прервать обработку в некоторой глубоко
вложенной структуре и выйти сразу из двух или б\'{o}льшего числа вложенных
циклов. Инструкция \verb|break| здесь не поможет, так как она осуществляет выход
только из самого внутреннего цикла. В качестве примера рассмотрим следующую
конструкцию:

\begin{LongCodeParWithCC}{\\\[\]}
    for (...)
        for (...) {
            ...
            if (disaster)          /* если бедствие */
                goto error;        /* уйти на ошибку */
        }
    ...
error:                             /* обработка ошибки */
    \textit[ликвидировать беспорядок]
\end{LongCodeParWithCC}

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

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

В качестве ещё одного примера рассмотрим такую задачу: определить, есть ли в
массивах \verb|a| и \verb|b| совпадающие элементы. Один из возможных вариантов
её реализации имеет следующий вид:

\begin{LongCodePar}
    for (i = 0; i < n; i++)
        for (j = 0; j < m; j++)
            if (a[i] == b[j])
                goto found;
    /* нет одинаковых элементов */
    ...
found:
    /* обнаружено совпадение: a[i] == b[j] */
    ...
\end{LongCodePar}

Программу нахождения совпадающих элементов можно написать и без \verb|goto|,
правда, заплатив за это дополнительными проверками и ещё одной переменной:

\begin{LongCodePar}
    found = 0;
    for (i = 0; i < n && !found; i++)
        for (j = 0; j < m && !found; j++)
            if (a[i] == b[j])
                found = 1;
    if (found)
        /* обнаружено совпадение: a[i-1] == b[j-1] */
        ...
    else
        /* нет одинаковых элементов */
        ...
\end{LongCodePar}

За исключением редких случаев, подобных только что приведённым,
программы с привлечением \verb|goto|, как правило, труднее для понимания и
сопровождения, чем программы решающие те же задачи без \verb|goto|. Хотя мы и не
догматики, всё же думается, что к \verb|goto| следует прибегать крайне редко,
если использовать вообще.
