logo search
Языки программирования

6.3. Операторы цикла

Операторы цикла наиболее трудны для программирования: в них легко сде­лать ошибку, особенно на границах цикла, то есть при первом и последнем выполнении тела цикла. Кроме того, неэффективная программа чаще всего расходует большую часть времени в циклах, поэтому так важно понимать их реализацию. Структура цикла показана на рис. 6.3. Оператор цикла имеет точ­ку входа, последовательность операторов, которые составляют цикл, и одну

или несколько точек выхода. Так как мы (обычно) хотим, чтобы наши циклы завершались, с точкой выхода бывает связано соответствующее условие, кото­рое определяет, следует сделать выход или продолжить выполнение цикла. Циклы различаются числом, типом и расположением условий выхода. Мы начнем с обсуждения циклов с произвольными условиями выхода, называ­емыми циклами while, а в следующем разделе обсудим частный случай — циклы for.

Наиболее общий тип цикла имеет единственный выход в начале цикла, т.е. в точке входа. Он называется циклом while:

C


while (s[i]. data != key)

Цикл while прост и надежен. Поскольку условие проверяется в начале цикла, мы знаем, что тело цикла будет полностью выполнено столько раз, сколько потребуется по условию. Если условие выхода сначала имеет значение False,то тело цикла не будет выполнено, и это упрощает программирование гранич­ных условий:

C


while (count > 0) process(s[count].data);

Если в массиве нет данных, выход из цикла произойдет немедленно.

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

Pascal

repeat

read(v);

put_in_table(v);

until v = end_value;

В языке Pascal repeat заканчивается, когда условие выхода принимает зна­чение True. He путайте его с циклом do в языке С, который заканчивается, когда условие выхода принимает значение False:

C

do{

v = get();

put_in_table(v);

} while (v != end_value);

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

while not found do

Pascal

begin

(* Длинное вычисление *)

(* Обнаружена ошибка, выход *)

(* Длинное вычисление *)

end

Pascal, в котором не предусмотрен выход из середины цикла, использует сле­дующее неудовлетворительное решение: установить условие выхода и ис­пользовать if-оператор, чтобы пропустить оставшуюся часть цикла:

while not found do

Pascal

begin

(* Длинное вычисление *)

if error_detected then found := True

else

begin

(* Длинное вычисление *)

end

end

В языке С можно использовать оператор break:

while (!found) {

C

/* Длинное вычисление */

if (error_detected()) break;

/* Длинное вычисление */

}

В Ada есть обычный цикл while, а также оператор exit, с помощью которого можно выйти из цикла в любом месте; как правило, пара связанных операторов if и exit заменяется удобной конструкцией when:

while not Found loop

Ada

-- Длинное вычисление

exit when error_detected;

- Длинное вычисление

end loop;

Операционная система или система, работающая в реальном масштабе време­ни, по замыслу, не должна завершать свою работу, поэтому необходим способ задания бесконечных циклов. В Ada это непосредственно выражается опера­тором loop без условия выхода:

Ada

loop

end loop;

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

while(1==1){

C

}

Реализация

Цикл while:

C


while (expression)

statement;

реализуется так:

L1: compute R1.expr

jump_zero R1,L2 Выйти из цикла, если false

statement Тело цикла

jump L1 Перейти на проверку завершения цикла L2:

Обратите внимание, что в реализации цикла while есть две команды перехода! Интересно, что если выход находится в конце цикла, то нужна только одна команда перехода:

do{

C

statement;

} while (expression);

компилируется в

L1: statement

compute expr

jump_nz L1 He ноль — это True

Хотя цикл while очень удобен с точки зрения читаемости программы, эф­фективность кода может быть увеличена путем замены его на цикл do. Для выхода из середины цикла требуются два перехода точно так же, как и для цикла while.