Menu

Цикл for в C++: синтаксис, примеры и распространённые ошибки

Как повторять код с помощью цикла for в C++: заголовок из трёх частей, счёт вверх и вниз, перебор массивов, вложенность, break и continue, а также ошибки смещения на единицу и беззнаковых типов, на которых попадаются все.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Зачем нужен цикл for

switch выбирает одну ветку и выполняет её один раз. Но настоящим программам нужно делать что-то многократно: вывести каждый результат, просуммировать список чисел, нарисовать 10 строк сетки. Цикл for — рабочая лошадка C++ для повторения кода известное число раз, со встроенным счётчиком, которым вы управляете.

Всё, что нужно циклу for, помещается в один компактный заголовок, поэтому вся история «сколько раз и как» видна с первого взгляда.

Заголовок из трёх частей

Заголовок цикла for состоит из трёх частей, разделённых точкой с запятой: инициализатор, условие и обновление.

for (initializer; condition; update) {
    // тело - выполняется, пока условие истинно
}

Они выполняются в определённом порядке: инициализатор выполняется один раз в начале; затем условие проверяется перед каждой итерацией; тело выполняется только если условие true; а обновление выполняется в конце каждой итерации, прямо перед тем, как условие снова проверяется.

Здесь int i = 0 выполняется один раз. Затем проверяется i < 5: пока оно истинно, тело выводит значение, а i++ увеличивает счётчик. Когда i достигает 5, условие становится false, поэтому цикл завершается и выводится done. Тело выполняется ровно 5 раз, при этом i принимает значения от 0 до 4.

Объявление счётчика внутри заголовка (int i = 0) ограничивает область видимости i циклом — он не существует после закрывающей фигурной скобки, что как раз то, что вам нужно.

Счёт вверх, вниз и с шагом

Часть обновления не ограничивается i++. Можно считать вниз, шагать на любую величину или перебирать индекс массива.

Первый цикл выполняется, пока i > 0, уменьшая счётчик каждый раз, поэтому выводит 5 4 3 2 1. Второй прибавляет 2 на каждом проходе и использует <= 10, потому что 10 — значение, которое мы хотим включить. Согласуйте условие с обновлением: счёт вниз сочетается с > или >=, счёт вверх — с < или <=.

Перебор массива

Самое частое применение счётного цикла — обход массива по индексу. Счётчик заодно служит позицией, которую вы читаете.

Обратите внимание, что условие — i < n, а не i <= n. У массива из 5 элементов допустимые индексы от 0 до 4; индекс 5 находится за концом. Чтение scores[5]неопределённое поведение: может вывестись мусор, программа упадёт или, казалось бы, сработает, незаметно повредив память. Шаблон i < n — безопасный вариант по умолчанию для любого массива с отсчётом от нуля.

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

break и continue

Два ключевых слова позволяют менять ход выполнения посреди цикла. break немедленно выходит из цикла; continue пропускает остаток текущей итерации и переходит к обновлению.

Первый цикл останавливается в тот момент, когда находит 7, и не проверяет остальное. Второй использует continue, чтобы пропускать вывод в теле всякий раз, когда i чётное, — обновление i++ всё равно выполняется, поэтому цикл продолжает двигаться. Тонкая ловушка: continue переходит к обновлению, поэтому если вы когда-нибудь полагаетесь на continue в цикле, чей счётчик обновляется внутри тела, а не в заголовке, вы можете случайно пропустить это обновление и зациклиться навсегда.

Вложенные циклы

Поместите один for внутрь другого, чтобы работать с сетками, таблицами или парами. Внутренний цикл полностью отрабатывает на каждом отдельном шаге внешнего цикла.

Это выводит таблицу умножения 3x3. Внешний цикл фиксирует row, внутренний цикл проходит по каждому col для этой строки, а затем перевод строки завершает строку. Давайте счётчикам разные имена (row/col, а не i/i) — повторное использование одного имени затеняет внешний и порождает непонятные баги. Следите и за стоимостью: вложение цикла на n внутрь цикла на n выполняет тело n * n раз, что быстро накапливается.

Распространённые подводные камни

Несколько ловушек объясняют большинство багов циклов for в C++:

  • Смещение на единицу: i <= n при размере с отсчётом от нуля читает один элемент за концом. Используйте i < n.
  • Беззнаковое переполнение снизу (underflow): счёт вниз с беззнаковым типом никогда не уходит в отрицательные. for (size_t i = n - 1; i >= 0; i--) зацикливается навсегда, потому что i >= 0 всегда истинно для беззнакового значения — когда i равно 0, i-- оборачивается в огромное положительное число. Используйте знаковый int для убывающего счёта или перепишите условие.
  • Изменение счётчика внутри тела: изменение i и в теле, и в заголовке делает число итераций непредсказуемым. Выберите одно место.
// BUG: infinite loop - unsigned i is never < 0
for (size_t i = n - 1; i >= 0; i--) {
    process(arr[i]);
}

Счётчики с плавающей точкой — другая тихая опасность: for (double x = 0.0; x != 1.0; x += 0.1) может никогда не попасть точно в 1.0, потому что 0.1 нельзя сохранить точно. Делайте цикл по целочисленному счёту и вычисляйте значение внутри, либо используйте < вместо !=.

Далее: циклы while

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

Часто задаваемые вопросы

Как написать цикл for в C++?

Поместите в заголовок три части, разделённые точкой с запятой: инициализатор, условие и обновление. for (int i = 0; i < 5; i++) { cout << i; } выполняет тело, при этом i принимает значения 0, 1, 2, 3, 4. Цикл останавливается, как только условие становится false.

В чём разница между циклом for и циклом for на основе диапазона в C++?

Классический for даёт вам счётчик индекса, которым вы управляете (for (int i = 0; i < n; i++)); он нужен, когда вам важна позиция или вы хотите шагать особым образом. Цикл for на основе диапазона (for (int x : v)) скрывает индекс и просто отдаёт вам каждый элемент — чище, когда нужны только значения.

Почему мой цикл for в C++ выполняется на один раз больше или меньше, чем нужно?

Это классическая ошибка смещения на единицу (off-by-one). Использование <= вместо < при размере с отсчётом от нуля выполняет одну лишнюю итерацию и читает за концом массива; использование <, когда вы хотели включить последнее значение, выполняет на одну итерацию меньше. Для массива размера n безопасный шаблон — for (int i = 0; i < n; i++).

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ