Циклы: for, while, do-while

Урок разбирает три цикла C — for, while и do-while, их внутреннее устройство, управление через break и continue и самые частые ошибки: бесконечный цикл и ошибку на единицу.
Цикл for в C — это не «перебор элементов», а три самостоятельные части: инициализация, условие и шаг. Понимание этой тройки объясняет всё поведение цикла.

Самый частый цикл — for. Его заголовок состоит из трёх частей, разделённых точкой с запятой:

for (int i = 0; i < 5; i++) {
    printf("%d ", i);    // 0 1 2 3 4
}

Первая часть (int i = 0) выполняется один раз в начале. Вторая (i < 5) проверяется перед каждым проходом — пока истинна, цикл работает. Третья (i++) выполняется после каждого прохода. Цикл while проще — только условие:

int i = 0;
while (i < 5) {
    printf("%d ", i);
    i++;
}

Цикл do-while проверяет условие В КОНЦЕ, поэтому тело выполняется хотя бы раз — это удобно для меню и повторного ввода:

int n;
do {
    printf("Введите положительное число: ");
    scanf("%d", &n);
} while (n <= 0);

Как работает под капотом

Покажем порядок выполнения частей for наглядно — это снимает почти все вопросы о циклах:

for (i = 0; i < 3; i++) { тело; }

   [1] i = 0          (один раз, в самом начале)
        |
        v
   [2] i < 3 ?  --НЕТ--> выход из цикла
        | ДА
        v
   [3] тело цикла
        |
        v
   [4] i++            (шаг)
        |
        +----> назад к [2]

Порядок: 1 -> 2 -> 3 -> 4 -> 2 -> 3 -> 4 -> ... -> 2(нет) -> выход

Операторы break и continue управляют ходом цикла: break немедленно выходит из цикла целиком, continue пропускает остаток тела и переходит к следующей итерации (включая шаг и проверку).

Частые ошибки

  • Бесконечный цикл. Если забыть менять переменную (i++) или условие никогда не становится ложным — программа зависает. while (1) без break внутри — тоже навсегда.
  • Ошибка на единицу (off-by-one). i <= n вместо i < n делает на один проход больше — и выходит за границу массива.
  • Точка с запятой после for. for (...); создаёт цикл с пустым телом, а следующий блок выполнится один раз.
  • Изменение счётчика внутри for и в шаге одновременно — легко потерять контроль над количеством итераций.

Best practices

  • Используйте for, когда число итераций известно заранее, и while, когда выход зависит от условия.
  • Объявляйте счётчик прямо в for: for (int i = 0; ...) — переменная не «утечёт» наружу и не помешает другому коду.
  • Граница — почти всегда i < n, а не i <= n. Эта привычка спасает от выхода за пределы массива.

Все три вида циклов есть и в Python, поэтому логику разных границ удобно проверить там. Заметьте классическую ошибку off-by-one.

# Сравним правильную и ошибочную границу цикла
arr = [10, 20, 30, 40, 50]   # 5 элементов, индексы 0..4

print("Правильно (i < len):")
for i in range(len(arr)):        # как for(i=0; i<5; i++) в C
    print(" ", arr[i])

print("Сумма через while:")
s, i = 0, 0
while i < len(arr):
    s += arr[i]
    i += 1
print("  итого:", s)

Та же логика на Python ▶ — в C обращение arr[5] при индексах 0..4 не вызовет ошибку, а молча прочитает чужую память. Поэтому граница i < len критична.

Цена итерации и кэш процессора

Циклы — это не только логика, но и производительность, и в C она видна особенно ясно. Современный процессор читает память не по одному байту, а блоками (кэш-линиями), поэтому обход данных подряд, в порядке их расположения в памяти, в разы быстрее, чем «прыжки» по случайным адресам. Для массива это означает: проход for (i = 0; i < n; i++) по соседним элементам максимально дружелюбен к кэшу. Когда мы дойдём до двумерных массивов, вы увидите, что порядок вложенных циклов влияет на скорость именно по этой причине. В высокоуровневых языках такие детали скрыты, а в C вы управляете ими напрямую — и потому C остаётся выбором там, где важна каждая микросекунда: в обработке сигналов, графике, играх и научных расчётах.

Итоги

В C три цикла: for (три части: инициализация, условие, шаг), while (только условие, проверка в начале) и do-while (проверка в конце, тело хотя бы раз). Управление — через break и continue. Главные ошибки — бесконечный цикл и off-by-one; защита — менять счётчик аккуратно и использовать границу i < n.

Проверьте себя
1. Чем do-while отличается от while?
Ado-while работает быстрее
Bdo-while проверяет условие в конце, поэтому тело выполняется хотя бы один раз
Cdo-while не может содержать break
Ddo-while перебирает только массивы
2. Что такое ошибка «off-by-one» в цикле по массиву из n элементов?
AЦикл работает в два раза медленнее
BИспользование i <= n вместо i < n даёт лишний проход и выход за границу массива
CЦикл не запускается вовсе
DСчётчик становится отрицательным