"Сквозное" выполнение ветвей caseвызывает смешанные чувства. С одной стороны, это хорошо, поскольку позволяет несколько ветвей caseобъединить в одну, как мы и поступили с цифрами в нашем примере. Но с другой - это означает, что в конце почти каждой ветви придется ставить break, чтобы избежать перехода к следующей. Последовательный проход по ветвям - вещь ненадежная, это чревато ошибками, особенно при изменении программы. За исключением случая с несколькими метками для одного вычисления, старайтесь по возможности реже пользоваться сквозным проходом, но если уж вы его применяете, обязательно комментируйте эти особые места.
Добрый вам совет: даже в конце последней ветви (после defaultв нашем примере) помещайте инструкцию break, хотя с точки зрения логики в ней нет никакой необходимости. Но эта маленькая предосторожность спасет вас, когда однажды вам потребуется добавить в конец еще одну ветвь case.
Упражнение 3.2. Напишите функцию escape (s,t), которая при копировании текста из t в s преобразует такие символы, как новая строка и табуляция в "видимые последовательности символов" (вроде \n и \t). Используйте инструкцию switch. Напишите функцию, выполняющую обратное преобразование эскейп- последовательностей в настоящие символы.
Мы уже встречались с циклами whileи for. В цикле
while ( выражение )
инструкция
вычисляется выражение . Если его значение отлично от нуля, то выполняется инструкция , и вычисление выражения повторяется. Этот цикл продолжается до тех пор, пока выражение не станет равным нулю, после чего вычисления продолжатся с точки, расположенной сразу за инструкцией .
Инструкция for
for ( выр 1 ; выр 2 ; выр 3 )
инструкция
эквивалентна конструкции
выр 1 ;
while ( выр 2 ) {
инструкция
выр 3 ;
}
если не считать отличий в поведении инструкции continue, речь о которой пойдет в параграфе 3.7.
С точки зрения грамматики три компоненты цикла forпредставляют собой произвольные выражения, но чаще выр 1 и выр 3 - это присваивания или вызовы функций, а выр 2 - выражение отношения. Любое из этих трех выражений может отсутствовать, но точку с запятой опускать нельзя. При отсутствии выр 1 , или выр 3 считается, что их просто нет в конструкции цикла; при отсутствии выр 2 , предполагается, что его значение как бы всегда истинно. Например,
for (;;) {
:
}
есть "бесконечный" цикл, выполнение которого, вероятно, прерывается каким-то другим способом, например с помощью инструкций breakили return. Какой цикл выбрать: whileили for- это дело вкуса. Так, в
while ((c = getchar()) == ' ' || c == '\n' || c == '\t')
; /* обойти символы-разделители */
нет ни инициализации, ни пересчета параметра, поэтому здесь больше подходит while.
Там, где есть простая инициализация и пошаговое увеличение значения некоторой переменной, больше подходит цикл for, так как в этом цикле организующая его часть сосредоточена в начале записи. Например, начало цикла, обрабатывающего первые n элементов массива, имеет следующий вид:
for (i = 0; i ‹ n; i++)
…
Это похоже на DO-циклы в Фортране и for-циклы в Паскале. Сходство, однако, не вполне точное, так как в Си индекс и его предельное значение могут изменяться внутри цикла, и значение индекса i после выхода из цикла всегда определено. Поскольку три компонента цикла могут быть произвольными выражениями, организация for-циклов не ограничивается только случаем арифметической прогрессии. Однако включать в заголовок цикла вычисления, не имеющие отношения к инициализации и инкрементированию, считается плохим стилем. Заголовок лучше оставить только для операций управления циклом.
В качестве более внушительного примера приведем другую версию программы atoi , выполняющей преобразование строки в ее числовой эквивалент. Это более общая версия по сравнению с рассмотренной в главе 2, в том смысле, что она игнорирует левые символы-разделители (если они есть) и должным образом реагирует на знаки + и -, которые могут стоять перед цифрами. (В главе 4 будет рассмотрен вариант atof , который осуществляет подобное преобразование для чисел с плавающей точкой.)
Читать дальше