Программирование на C и C++

Онлайн справочник программиста на C и C++

Вариации цикла for

Мы рассмотрели наиболее типичный вид оператора for. Тем не менее, имеется несколько вариаций, увеличивающих мощь и гибкость в некоторых ситуациях.

Одна из наиболее типичных вариаций достигается с использованием оператора «запятая», тем самым позволяя иметь две или более переменных цикла. (Следует помнить, что оператор «запятая» используется для объединения нескольких выражений. Например, данный цикл использует переменные х и у для управления циклом и обе эти переменные инициализируются в операторе for:

for (х = 0, у = 0; х + у < 10; ++х)
{
scanf("%d", &у);

...

}

Здесь оператор «запятая» разделяет два инициализационных оператора. При каждом увеличении х цикл повторяется и значение у вводится с клавиатуры. Как х, так и у должны иметь корректное значение для окончания цикла. Необходимо инициализировать переменную у нулем, поэтому ее значение определяется перед первым вычислением выражения условия. Если бы у не была определена, то имелся бы шанс, что в результате предыдущей работы программы она содержала 10, тем самым делая условие проверки ложным и запрещая выполнения тела цикла.

Другой пример использования нескольких переменных цикла можно найти в показанной ниже функции reverse(). Она предназначена для копирования содержимого первого строкового аргумента во второй строковый аргумент в обратном порядке. Например, если функция вызывается с параметром «hello» для s, то после окончания работы r получит «olleh»:

/* Копирование s в r в обратном порядке */

void reverse (char *s, char *r)
{
int i, j;
for (i=strlen(s)-1, j=0; i>=0; j++,i--) r[i] = s[j] ;
r[j] =  '\0'; /* присоединение завершающего символа */
}

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

void sign_on (void)
{
char str[20];
int x;
for(x=0; x<3 && strcmp (str, "password"); ++x) {
printf("enter password please:");
gets(str);
}
if(x==3) hang_up();
}

Надо помнить, что strcmp() - это стандартная библиотечная функция, выполняющая сравнение двух строк и возвращающая 0 в случае совпадения.

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

#include <stdio.h>

int readnuro(void), prompt(void);
int sqrnum(int num);

int main(void)
{
int t;
for(prompt(); t=readnum(); prompt()) sqrnum(t);
return 0;
}

int prompt(void)
{
printf (": ");
return 0;
}

int readnum(void)
{
int t;
scanf ("%d", &t);
return t;
}

int sqrnum(int num)
{
printf("%d\n", num*num);
return 0;
}

Если посмотреть внимательно на цикл for в main(), то можно увидеть, что каждая часть цикла содержит вызовы функций, которые осуществляют подсказку и чтение вводимого с клавиатуры числа. Если введенное число равно нулю, цикл оканчивается, поскольку условие ложно, иначе число возводится в квадрат. Следовательно, в данном цикле части инициализации и увеличения используются не традиционно, но абсолютно корректно.

Еще один интересный момент цикла for - это необязательность наличия какой-либо части. Практически можно опустить любую часть. Например, следующий цикл работает до тех пор, пока не будет введено 123:

for (х=0; х != 123; ) scanf ("%d", &х);

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

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

gets (s); /* чтение строки в s */
if(*s) х = strlen(s); /* получение длины строки */
for( ;х<10; )
{
printf ("%d", х);
}

Здесь часть инициализации пуста и х инициализируется до входа в цикл.