Страница 64 из 82 Примеры использования оператора цикла for
Рассмотрим несколько примеров. Так, в ходе выполнения оператора цикла int i; for (i = 0; i < 10; i++) { int j = 0; j += i; }
десять раз будет выполняться оператор определения переменной j. Каждый раз это будут новые объекты. Каждый раз новой переменной заново будет присваиваться новое значение одной и той же переменной i, объявленной непосредственно перед оператором цикла for. Объявление переменной i можно расположить непосредственно в теле оператора-инициализатора цикла: for (int i = 0; i < 10; i++) { int j = 0; j += i; }
И здесь возникает одна проблема. Дело в том, что тело оператора цикла for (оператор или блок операторов) имеет ограниченную область действия имён. А область действия имени, объявленного в операторе-инициализаторе, оказывается шире этой области. Заголовок цикла for в C++ - центр управления циклом. Здесь следят за внешним миром, за тем, что происходит вне цикла. И потому все обращения к переменным и даже их новые объявления в заголовке цикла относятся к "внешней" области видимости. Следствием такого допущения (его преимущества далеко не очевидны) является правило соотнесения имени, объявленного в заголовке и области его действия. По отношению к объявлению переменной в заголовке оператора цикла for, правило соотнесения гласит, что область действия имени, объявленного в операторе инициализации цикла for, располагается в блоке, содержащем данный оператор цикла for. А вот область действия имени переменной j при этом остаётся прежней. В теле оператора for может быть определена одноимённая переменная: for (int i = 0; i < 10; i++) { int i = 0; i += i; }
Пространство имени переменной в операторе цикла ограничено блоком из двух операторов. В этом пространстве переменная, объявленная в заголовке, оказывается скрытой одноимённой переменной. Десять раз переменная i из оператора-инициализатора цикла будет заслоняться одноимённой переменной из оператора тела цикла. И всякий раз к нулю будет прибавляться нуль. Ещё один пример. Два расположенных друг за другом оператора цикла for содержат ошибку for (int i = 0, int j = 0; i < 100; i++, j--) { // Операторы первого цикла. } for (int i = 0, int k = 250; i < 100; i++, k--) { // Операторы второго цикла. }
Всё дело в том, что, согласно правилу соотнесения имён и областей действия имён в операторе цикла for, объявления переменных в заголовке цикла оказываются в общем пространстве имён. А почему, собственно, не приписать переменные, объявленные в заголовке цикла блоку, составляющему тело цикла? У каждого из альтернативных вариантов соотнесения имеются свои достоинства и недостатки. Однако выбор сделан, что неизбежно ведёт к конфликту имён и воспринимается как попытка переобъявления ранее объявленной переменной. Эту самую пару операторов for можно переписать, например, следующим образом: for (int i = 0, int j = 0; i < 100; i++, j--) { // Здесь располагаются операторы первого цикла. } for (i = 0, int k = 250; i < 100; i++, k--) { // Здесь располагаются операторы второго цикла. } Здесь нет ошибок, но при чтении программы может потребоваться дополнительное время для того, чтобы понять, откуда берётся имя для выражения присвоения i = 0 во втором операторе цикла. Кроме того, если предположить, что операторы цикла в данном контексте реализуют независимые шаги какого-либо алгоритма, то почему попытка перемены мест пары абсолютно независимых операторов сопровождается сообщением об ошибке: for (i = 0, int k = 250; i < 100; i++, k--) { // Здесь располагаются операторы второго цикла. } for (int i = 0, int j = 0; i < 100; i++, j--) { // Здесь располагаются операторы первого цикла. }
Очевидно, что в первом операторе оказывается необъявленной переменная i. Возможно, что не очень удобно, однако, в противном случае, в центре управления циклом трудно буден следить за внешними событиями. В конце концов, никто не заставляет программиста располагать в операторе инициализации объявления переменных. Исходная пара операторов может быть с успехом переписана следующим образом: int i, j, k; ::::: for (i = 0, k = 250; i < 100; i++, k--) { // Здесь располагаются операторы второго цикла. } for (i = 0, j = 0; i < 100; i++, j--) { // Здесь располагаются операторы первого цикла. }
А вот ещё один довольно странный оператор цикла, в котором, тем не менее, абсолютно корректно соблюдены принципы областей действия имён, областей видимости имён, а также соглашения о соотнесении имён и областей их действия: for (int x; x < 10; x++) {int x = 0; x++;} Так что не забываем о том, что область действия имён в заголовке цикла шире от области действия имён в теле цикла. И вообще, если можно, избавляемся от объявлений в заголовке оператора цикла. Оператор цикла do … while называется оператором цикла с постусловием. От циклов с предусловием он отличается тем, что сначала выполняется оператор (возможно, составной), а затем проверяется условие выполнения цикла, представленное выражением, которое располагается в скобках после ключевого слова while. В зависимости от значения этого выражения возобновляется выполнение оператора. Таким образом, всегда, по крайней мере один раз, гарантируется выполнение оператора цикла. int XXX = 0; do {cout << XXX << endl; XXX++;} while (XXX < 0); |