5.5. Инструкция цикла for

5.5. Инструкция цикла for

Как мы видели, выполнение программы часто состоит в повторении последовательности инструкций - до тех пор, пока некоторое условие остается истинным. Например, мы читаем и обрабатываем записи файла, пока не дойдем до его конца, перебираем элементы массива, пока индекс не станет равным размерности массива минус 1, и т.д. В С++ предусмотрено три инструкции для организации циклов, в частности for и while, которые начинаются проверкой условия. Такая проверка означает, что цикл может закончиться без выполнения связанной с ним простой или составной инструкции. Третий тип цикла, do while, гарантирует, что тело будет выполнено как минимум один раз: условие цикла проверяется по его завершении. (В этом разделе мы детально рассмотрим цикл for; в разделе 5.6 разберем while, а в разделе 5.7 - do while.)

Цикл for обычно используется для обработки структур данных, имеющих фиксированную длину, таких, как массив или вектор:

#include vector

int main() {

int ia[ 10 ];

for ( int ix = 0; ix 10; ++-ix )

ia[ ix ] = ix;

vectorint ivec( ia, ia+10 );

vectorint::iterator iter = ivec.begin() ;

for ( ; iter != ivec.end(); ++iter )

*iter *= 2;

return 0;

}

Синтаксис цикла for следующий:

for (инструкция-инициализации; условие; выражение )

инструкция

инструкция-инициализации может быть либо выражением, либо инструкцией объявления. Обычно она используется для инициализации переменной значением, которое увеличивается в ходе выполнения цикла. Если такая инициализация не нужна или выполняется где-то в другом месте, эту инструкцию можно заменить пустой (см. второй из приведенных ниже примеров). Вот примеры правильного использования инструкции-инициализации:

// index и iter определены в другом месте

for ( index =0; ...

for ( ; /* пустая инструкция */ ...

for ( iter = ivec.begin(); ...

for ( int 1o = 0,hi = max; ...

for ( char *ptr = getStr(); ...

условие служит для управления циклом. Пока условие при вычислении дает true, инструкция продолжает выполняться. Выполняемая в цикле инструкция может быть как простой, так и составной. Если же самое первое вычисление условия дает false, инструкция не выполняется ни разу. Правильные условия можно записать так:

(... index arraySize; ... )

(... iter != ivec.end(); ... )

(... *stl++ = *st2++; ... )

(... char ch = getNextChar(); ... )

Выражение вычисляется после выполнения инструкции на каждой итерации цикла. Обычно его используют для модификации переменной, инициализированной в инструкции-инициализации. Если самое первое вычисление условия дает false, выражение не выполняется ни разу. Правильные выражения выглядят таким образом:

( ... ...; ++-index )

( ... ...; ptr = ptr-next )

( ... ...; ++i, --j, ++cnt )

( ... ...; ) // пустое выражение

Для приведенного ниже цикла for

const int sz = 24;

int ia[ sz ];

vectorint ivec( sz );

for ( int ix = 0; ix sz; ++ix ) {

ivec[ ix ] = ix;

ia[ ix ]= ix;

}

порядок вычислений будет следующим:

* инструкция-инициализации выполняется один раз перед началом цикла. В данном примере объявляется переменная ix, которая инициализируется значением 0.

* Вычисляется условие. Если оно равно true, выполняется составная инструкция тела цикла. В нашем примере, пока ix меньше sz, значение ix присваивается элементам ivec[ix] и ia[ix]. Когда значением условия станет false, выполнение цикла прекратится. Если самое первое вычисление условия даст false, составная инструкция выполняться не будет.

* Вычисляется выражение. Как правило, его используют для модификации переменной, фигурирующей в инструкции-инициализации и проверяемой в условии. В нашем примере ix увеличивается на 1.

Эти три шага представляют собой полную итерацию цикла for. Теперь шаги 2 и 3 будут повторяться до тех пор, пока условие не станет равным false, т.е. ix окажется равным или большим sz.

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

for ( int ival = 0, *pi = ia, ri = val;

ival size;

++iva1, ++pi, ++ri )

// ...

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

#include iostream

int main()

{

for ( int ix = 0;

bool done = ix == 10;

++ix )

cout "ix: " ix endl;

}

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

int main()

{

string word;

vector string text;

// ...

for ( vector string ::iterator

iter = text.begin(),

iter_end = text.end();

iter != text.end(); ++iter )

{

if ( *iter == word )

break;

// ...

}

// ошибка: iter и iter_end невидимы

if ( iter != iter_end )

// ...

Упражнение 5.8

Допущены ли ошибки в нижеследующих циклах for? Если да, то какие?

(a)

for ( int *ptr = ia, ix = 0;

ix size ptr != ia+size;

++ix, ++ptr )

// ...

(b)

for ( ; ; ) {

if ( some_condition )

break;

// ...

}

(c)

for ( int ix = 0; ix sz; ++ix )

// ...

if ( ix != sz )

// ...

(d)

int ix;

for ( ix sz; ++ix )

// ...

(e)

for ( int ix = 0; ix sz; ++ix, ++ sz )

// ...

Упражнение 5.9

Представьте, что вам поручено придумать общий стиль использования цикла for в вашем проекте. Объясните и проиллюстрируйте примерами правила использования каждой из трех частей цикла.

Упражнение 5.10

Дано объявление функции:

bool is_equa1( const vectorint vl,

const vectorint v2 );

Напишите тело функции, определяющей равенство двух векторов. Для векторов разной длины сравнивайте только то количество элементов, которое соответствует меньшему из двух. Например, векторы (0,1,1,2) и (0,1,1,2,3,5,8) считаются равными. Длину векторов можно узнать с помощью функций v1.size() и v2.size().