Menu

break и continue в C: как они работают и когда их применять

break выходит из цикла или switch; continue переходит к следующему проходу. Как каждый из них ведёт себя в for, while и do-while, как выбраться из вложенных циклов с помощью флага или goto и какие злоупотребления делают цикл нечитаемым.

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

Два способа изменить ход цикла

Условие цикла решает, когда он закончится, но иногда настоящий ответ обнаруживается в середине тела. Вы ищете элемент в массиве и находите его третьим — зачем проверять остальные 997? Или вы обрабатываете список, и одна запись пустая: пропустите её и идите дальше.

В C есть по ключевому слову на каждый случай:

  • break — выйти из цикла прямо сейчас. Выполнение продолжится с первой инструкции после него.
  • continue — прервать только этот проход. Перейти к следующему.

Первый цикл выводит 1 2 3 4 и останавливается. Второй выводит 1 2 3 4 6 7 8 9 10 — он пропустил одно значение и продолжил работу.

break в цикле

Канонический случай — поиск, который прекращается сразу после успеха:

Поскольку found устанавливается до break, код после цикла может отличить «нашли» от «элементы кончились». Эта пара — переменная результата плюс break — и есть стандартная идиома поиска в C. Сам счётчик цикла после него использовать нельзя, если он объявлен в заголовке: он выходит из области видимости на закрывающей скобке.

break всегда выходит из одного уровня: ближайшего охватывающего for, while, do-while или switch.

continue в цикле

continue нужен для записей, которые следует пропустить, а не прерывать из-за них работу:

Куда именно переходит continue, зависит от вида цикла:

for        -> к части обновления в заголовке (счётчик всё равно двигается)
while      -> к проверке условия (больше ничего в теле не выполняется)
do-while   -> к проверке условия внизу

Эта разница не академическая. В цикле for continue не может пропустить увеличение счётчика, потому что оно стоит в заголовке. В цикле while — может:

/* БАГ: continue пропускает i++, поэтому всё зависает на первом чётном числе */
int i = 0;
while (i < 10) {
    if (i % 2 == 0) {
        continue;
    }
    printf("%d ", i);
    i++;
}

Либо перенесите i++ выше continue, либо перепишите всё как цикл for, где заголовок делает такую ошибку невозможной.

break в switch — это не break в цикле

break завершает ближайший охватывающий цикл или switch — что из них ближе. Поместите switch внутрь цикла, и каждый break в нём будет принадлежать switch:

Команда q печатает своё сообщение, а цикл продолжает работать и доходит до cc. Если выход должен был завершить цикл, этот break своей задачи не выполнил. Решения — флаг, goto или вынос цикла в функцию с return; все три показаны ниже.

Выход из вложенных циклов

Один break покидает один уровень. Внутри двух вложенных циклов он завершает только внутренний, а внешний начинает следующий проход, как будто ничего не случилось.

В C нет ни break 2, ни break с меткой. Есть три честных варианта:

Флаг. Переносимо, очевидно и слегка шумно:

Флаг появляется в двух местах — в условии внешнего цикла и в присваивании, — и это его цена. При трёх уровнях вложенности такой подход становится по-настоящему неприятным.

goto. Многократно обруганный переход, и это тот самый случай, когда опытные C-программисты применяют его без извинений:

Переход goto вперёд, на метку сразу после циклов, делает ровно то же, что break с меткой в других языках, — одной строкой и без флага, который нужно держать в согласованном состоянии. Дурная репутация goto заслужена переходами назад и прыжками внутрь других блоков, которые и правда порождают нечитаемый поток управления. Один переход вперёд из вложенных циклов — не тот случай. Ядро Linux использует тот же приём для путей освобождения ресурсов. Применяйте его осознанно и умеренно, но не позволяйте табу загонять себя в альтернативу с тремя флагами, которую читать сложнее.

Функция и return. Обычно лучший ответ, потому что поиск заслуживает имени:

return разворачивает все циклы функции сразу, не требует ни флага, ни метки, а у кода поиска теперь есть имя и его можно тестировать отдельно. Когда вложенность становится настолько глубокой, что выход из неё превращается в проблему, это часто намёк на то, что блок с самого начала хотел быть функцией.

Когда они помогают, а когда мешают

break и continue оправдывают себя, когда позволяют циклу один раз сказать «этот случай закончен» ближе к началу, вместо того чтобы заворачивать всё тело в if. Сравните:

/* с continue: основная работа не уходит вправо */
for (int i = 0; i < n; i++) {
    if (!isValid(a[i])) continue;
    if (isDuplicate(a[i])) continue;
    process(a[i]);
}

/* без него: каждая проверка добавляет уровень */
for (int i = 0; i < n; i++) {
    if (isValid(a[i])) {
        if (!isDuplicate(a[i])) {
            process(a[i]);
        }
    }
}

Они мешают, когда их много. У цикла с пятью разбросанными break и тремя continue условие выхода нигде не записано — читателю приходится мысленно прогонять всё тело, чтобы понять, когда цикл остановится. Два правила, которые хорошо себя показывают: держите проверки с continue вместе в начале тела и, если в цикле больше одного-двух break, подумайте, не должно ли было этим заниматься условие в заголовке.

Две ловушки помельче. continue в do-while переходит к проверке внизу, которая всё равно следующая по порядку, — безвредно, но удивляет. А break вне цикла и вне switch — ошибка компиляции, и это единственная ошибка из перечисленных, которую компилятор поймает за вас.

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

В чём разница между break и continue в C?

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

Как выйти из вложенного цикла в C?

Один break покидает только самый внутренний цикл. Чтобы выйти из обоих, либо заведите флаг и проверяйте его в условии внешнего цикла, либо используйте goto для перехода на метку после циклов, либо — обычно это чище всего — вынесите циклы в функцию и используйте return.

break выходит из цикла или только из switch?

Из того, что охватывает его теснее. break внутри switch, который находится внутри цикла, завершает только switch; цикл продолжает работать. Чтобы выйти из цикла изнутри switch, нужен флаг, goto или return.

Почему continue вызывает бесконечный цикл в моём while?

Потому что continue переходит к проверке условия и пропускает остаток тела — в том числе увеличение счётчика, если оно стоит в конце. В цикле for обновление живёт в заголовке, поэтому выполняется всегда; в цикле while перенесите обновление выше continue или используйте for.

Coddy programming languages illustration

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

НАЧАТЬ