Цикл — это управляющая конструкция, которая повторяет блок кода, пока истинно заданное условие. В C и C++ есть три цикла: for (со счетчиком), while (с предусловием, проверка до тела) и do-while (с постусловием, проверка после тела). Базовый синтаксис в обоих языках одинаков; отличается только range-based for — он есть в C++ с 2011 года и отсутствует в чистом C. Ниже — рабочие примеры каждого цикла, операторы break и continue, вложенные циклы и три типичные ошибки с их фактическим выводом. Все примеры проверены компилятором Apple clang 21 (стандарты C11 и C++11).
Содержание
Три вида циклов: когда какой брать
Выбор цикла зависит от того, известно ли заранее число повторений и нужен ли гарантированный первый проход.
| Цикл | Проверка условия | Когда брать |
|---|---|---|
for |
перед телом, счетчик рядом | число итераций известно (перебор диапазона, массива) |
while |
перед телом | повтор «пока выполняется условие», число шагов заранее неизвестно |
do-while |
после тела | нужен минимум один проход (меню, чтение ввода до валидного) |
Все три работают в C и C++ одинаково. Дальше — по одному минимальному примеру на каждый.
Цикл for (со счетчиком)
for объединяет три части в заголовке: инициализацию, условие и приращение. Инициализация выполняется один раз, условие проверяется перед каждым проходом, приращение — после тела.
#include <stdio.h>
int main(void) {
int sum = 0;
for (int i = 1; i <= 5; i++) { // i от 1 до 5 включительно
printf("i = %d\n", i);
sum += i;
}
printf("sum = %d\n", sum);
return 0;
}
Фактический вывод:
i = 1
i = 2
i = 3
i = 4
i = 5
sum = 15
Объявление счетчика прямо в заголовке (for (int i = ...) допустимо в C начиная со стандарта C99 и во всех версиях C++. В старом C89 переменную нужно было объявлять до цикла. Приращение i++ (постфиксный инкремент) увеличивает i на 1; для шага в обратную сторону пишут i--, для другого шага — например i += 2.
В C++ синтаксис тот же. Например, сумма чисел от 1 до 1000:
#include <iostream>
int main() {
long long total = 0;
for (int i = 1; i <= 1000; i++) {
total += i;
}
std::cout << total << "\n"; // выведет 500500
return 0;
}
Тип long long взят с запасом; для суммы до 1000 хватило бы и int, но на больших диапазонах узкий тип переполнится, а результат станет неверным без ошибки компиляции.
Цикл while (с предусловием)
while проверяет условие до тела. Если оно ложно с самого начала, тело не выполнится ни разу. Такой цикл удобен, когда число повторений заранее неизвестно и определяется условием.
#include <stdio.h>
int main(void) {
int n = 3;
while (n > 0) {
printf("осталось %d\n", n);
n--; // без этого шага условие никогда не станет ложным
}
printf("start\n");
return 0;
}
Фактический вывод:
осталось 3
осталось 2
осталось 1
start
Ключевой момент: внутри тела должно быть действие, которое рано или поздно сделает условие ложным (здесь n--). Забыть его — частая причина зацикливания, к этому вернемся в разделе про ошибки.
Цикл do-while (с постусловием)
do-while сначала выполняет тело, а условие проверяет после. Поэтому тело гарантированно отрабатывает хотя бы один раз, даже если условие изначально ложно.
#include <stdio.h>
int main(void) {
int i = 6;
do {
printf("%d ", i);
i--;
} while (i > 0); // точка с запятой после while обязательна
printf("\n");
return 0;
}
Фактический вывод:
6 5 4 3 2 1
Обратите внимание на точку с запятой после while (i > 0) — в do-while она обязательна, в отличие от обычного while. Этот цикл берут там, где первый проход нужен всегда: показать меню, прочитать ввод и повторить запрос, если он неверный.
break и continue
Два оператора меняют ход цикла. break немедленно выходит из цикла, continue пропускает остаток тела и переходит к следующей итерации.
break — выйти из цикла досрочно:
#include <stdio.h>
int main(void) {
for (int i = 1; i <= 100; i++) {
if (i > 5) {
break; // как только i стало 6 - выходим
}
printf("%d ", i);
}
printf("\n");
return 0;
}
Вывод: 1 2 3 4 5 — цикл заявлен до 100, но break обрывает его на шестом шаге.
continue — пропустить текущую итерацию. Сумма только нечетных чисел от 1 до 10:
#include <stdio.h>
int main(void) {
int sum = 0;
for (int i = 1; i <= 10; i++) {
if (i % 2 == 0) { // четное - остаток от деления на 2 равен 0
continue; // пропускаем, к сумме не прибавляем
}
sum += i;
}
printf("%d\n", sum); // 1+3+5+7+9 = 25
return 0;
}
Вывод: 25. Важная деталь: в цикле for continue переходит именно к приращению (i++), а не мимо него, поэтому счетчик не застревает. А вот в while с continue легко забыть увеличить счетчик перед пропуском — и получить бесконечный цикл.
Вложенные циклы
Цикл можно поместить внутрь другого. На каждый проход внешнего цикла полностью отрабатывает внутренний. Классический пример — таблица (двумерный перебор):
#include <stdio.h>
int main(void) {
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
printf("%d ", i * j);
}
printf("\n"); // перенос строки после каждой строки таблицы
}
return 0;
}
Фактический вывод:
1 2 3
2 4 6
3 6 9
break внутри вложенного цикла прерывает только тот цикл, в котором он написан, — внешний продолжит работу. В C и C++ нет break с меткой (как в Java); чтобы выйти сразу из обоих, используют флаг-переменную, вынос кода в функцию с return или, реже, goto.
range-based for — только в C++ (с C++11)
Отдельная особенность C++: цикл range-based for, который перебирает элементы контейнера или массива без явного индекса. В чистом C его нет — там перебор идет по индексу или указателю.
#include <iostream>
#include <vector>
int main() {
std::vector<int> nums = {10, 20, 30};
int sum = 0;
for (int x : nums) { // x поочередно принимает 10, 20, 30
sum += x;
}
std::cout << "sum = " << sum << "\n"; // sum = 60
for (const auto& x : nums) { // ссылка, без копирования элемента
std::cout << x << " ";
}
std::cout << "\n"; // 10 20 30
return 0;
}
Запись int x : nums копирует каждый элемент в x; для тяжелых объектов берут const auto& x, чтобы не копировать, а если элемент нужно менять — auto& x. Range-based for доступен с C++11; при компиляции более старым стандартом (-std=c++98) он не соберется. В C тот же перебор пишут через индекс: for (int i = 0; i < 3; i++) sum += nums[i];.
Типичные ошибки
Три ошибки встречаются у новичков чаще всего. Показываю каждую тройкой: неверный код, его фактический результат, исправление.
1. Off-by-one (промах на единицу). Нужно вывести числа от 1 до 5, но условие со строгим < теряет последнее значение:
for (int i = 1; i < 5; i++) { // хотели до 5, а получим до 4
printf("%d ", i);
}
// фактический вывод: 1 2 3 4
Исправление — i <= 5 (или i < 6), тогда вывод 1 2 3 4 5. Зеркальная версия этой ошибки опаснее: при переборе массива из n элементов условие i <= n вместо i < n читает элемент за границей массива. Индексы идут от 0 до n-1, а обращение к arr[n] — это неопределенное поведение (может быть мусор, а может и аварийное завершение).
2. Бесконечный цикл. Условие никогда не становится ложным, потому что счетчик не приближается к границе:
int i = 1;
while (i != 10) { // i идет 1, 3, 5, 7, 9, 11 ... и 10 пропускает
i += 2;
}
Здесь i проходит только нечетные значения и в точку 10 не попадает никогда — цикл не завершится. Исправление — использовать нестрогое сравнение while (i < 10) вместо проверки на точное равенство. Общее правило: сравнивать счетчик по </>, а не по ==/!=, если шаг может «перепрыгнуть» границу.
3. Модификация счетчика внутри тела. Счетчик меняют и в заголовке, и в теле — шаг фактически удваивается:
for (int i = 0; i < 6; i++) { // приращение i++ уже в заголовке
printf("%d ", i);
i++; // лишнее изменение счетчика в теле
}
// фактический вывод: 0 2 4
Вместо 0 1 2 3 4 5 получаем 0 2 4. Clang и GCC предупреждают об этом (-Wfor-loop-analysis: variable incremented both in the loop header and in the loop body), поэтому компилируйте с флагом -Wall и читайте предупреждения. Исправление — не трогать счетчик в теле цикла for; если нужен другой шаг, задайте его в заголовке (i += 2).
Выводы
- В C и C++ три цикла:
for(число итераций известно),while(проверка условия до тела) иdo-while(тело выполняется минимум один раз). - Базовый синтаксис циклов, а также
break(выход) иcontinue(следующая итерация) в обоих языках идентичны. - range-based for (
for (x : контейнер)) — особенность C++ с версии C++11; в чистом C его нет, там перебор идет по индексу или указателю. breakво вложенном цикле выходит только из своего цикла; в C/C++ нет break с меткой — нужен флаг,returnилиgoto.- Главные ловушки: off-by-one (
<против<=), сравнение счетчика по!=при шаге больше 1 (зацикливание) и лишнее изменение счетчика в теле. Компиляция с-Wallловит часть из них.
Где применяется / связь с практикой
Циклы — базовая механика любой программы на C и C++: перебор массивов и векторов, чтение файла построчно, игровой цикл, обработка ввода. Ошибки с границами и зацикливанием — самые частые у начинающих, и разбирать их лучше на практике, с компилятором и отладчиком под рукой.
Освойте тему на практике
Системно разобраться в языке, указателях, памяти и стандартной библиотеке помогает курс C++ Basic: там циклы и работа с данными идут в связке с реальными задачами и проверкой кода наставником. Прежде чем выбирать курс, можно посмотреть, как устроены занятия, на бесплатных открытых уроках — разбор конкретной темы в прямом эфире с преподавателем.
Смежные темы: Циклы в Bash.
FAQ
Чем for отличается от while, если оба перебирают числа?
Различие в стиле, а не в возможностях: любой for можно переписать через while и наоборот. for компактно держит счетчик, условие и шаг в одном заголовке — его берут, когда число итераций известно. while удобнее, когда повтор зависит от условия, а не от счетчика.
Можно ли объявлять переменную счетчика прямо в for в языке C?
Да, начиная со стандарта C99 (for (int i = 0; ...)). В старом C89 это запрещено — счетчик объявляли до цикла. В C++ объявление в заголовке допустимо во всех версиях.
Как выйти сразу из двух вложенных циклов?
Один break выходит только из своего цикла. Чтобы прервать оба, заводят флаг-переменную и проверяют ее во внешнем цикле, выносят код в функцию и делают return, либо используют goto на метку после циклов. В C и C++ break с меткой не поддерживается.



