JavaRush /Курсы /C++ SELF /do-while: хотя бы один раз

do-while: хотя бы один раз

C++ SELF
4 уровень , 3 лекция
Открыта

1. Знакомство с циклом do-while

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

Представьте типичную задачу: «спросить у пользователя число и повторять вопрос, пока он не введёт число больше нуля». Чтобы проверить “больше нуля”, нужно сначала это число прочитать. Значит, минимум один проход нам жизненно необходим: без него у нас просто нет значения для проверки, и do-while делает этот сценарий максимально прямолинейным.

Синтаксис do-while и обязательная ;

У do-while синтаксис простой, но с характером: он требует точку с запятой в конце. Это один из редких случаев в C++, когда компилятор как будто говорит: «я знаю, что это выглядит странно, но так надо». Важно запомнить целиком шаблон, а не собирать его по кусочкам на экзамене у компилятора. Форма выглядит так: сначала do, затем блок, затем while (условие) и обязательная ;.

Вот минимальный пример «выполним один раз и выйдем», потому что условие ложно:

#include <iostream>

int main() {
    int x = 0;

    do {
        std::cout << "Hello!\n"; // Hello!
    } while (x > 0);
}

Ключевая мысль: условие проверяется после тела. Поэтому тело гарантированно выполнится хотя бы один раз. И да, точка с запятой после while (...) обязательна — без неё будет ошибка компиляции.

while и do-while: одно отличие — разные последствия

Разница между while и do-while кажется маленькой, но последствия бывают очень заметны. В while мы сначала проверяем условие и только потом выполняем тело. В do-while мы сначала выполняем тело и только потом проверяем условие. Это влияет на то, выполнится ли тело хотя бы один раз, и часто именно этого нам и не хватает.

Сравним на одном и том же значении x = 0:

#include <iostream>

int main() {
    int x = 0;

    while (x > 0) {
        std::cout << "while\n";
    }

    do {
        std::cout << "do-while\n"; // do-while
    } while (x > 0);
}

Если вы читаете это как историю, то while говорит: «пока правда — делаю». А do-while говорит: «делаю, а потом решаю, повторять ли». Второй вариант логичен, когда первое действие необходимо само по себе: показать меню, запросить ввод, вывести подсказку, выполнить “первичную попытку”.

2. Когда do-while удобен в реальном коде

Ввод и меню без лишней акробатики

На практике do-while чаще всего встречается в двух сценариях. Первый — это «ввод с повтором»: спросить пользователя значение, проверить, подходит ли оно, и если нет — спросить снова. Второй — «консольное меню»: показать список команд, дать выбрать команду, выполнить действие, и снова показать меню, пока не выберут выход.

Заметьте: обе ситуации имеют общий смысл — без первого прохода ничего не работает. Пока меню не показали, выбирать нечего. Пока значение не прочитали, проверять нечего. Это как пытаться оценить вкус борща, не попробовав борщ (хотя некоторые так и делают, но это уже отдельная дисциплина).

Пример: просим число > 0

Начнём с простого: читаем n и не отпускаем пользователя, пока он не введёт n > 0. Это идеальный кейс для do-while, потому что нам нужно минимум один раз выполнить чтение.

#include <iostream>

int main() {
    int n = 0;

    do {
        std::cout << "Enter n (> 0): ";
        std::cin >> n;
    } while (n <= 0);

    std::cout << "n = " << n << '\n'; // например: n = 5
}

Обратите внимание: условие в конце читается почти как текст: «повторять, пока n <= 0». Это хороший стиль для do-while: формулировать условие именно как условие продолжения, а не выхода. Так вы меньше путаетесь в ! и в смыслах.

3. Пример: консольное меню «копилка»

Давайте начнём собирать небольшое консольное приложение, которое будет развиваться на протяжении тем про циклы. Сегодня мы сделаем основу: “копилка” (условный бюджет), куда можно добавить деньги, посмотреть баланс, сбросить баланс и выйти. Никаких массивов, функций и сложных структур — пока всё на int, if/else и циклах.

Скелет меню на do-while

Сначала сделаем скелет меню на do-while. Это удобно: меню должно показаться хотя бы один раз.

#include <iostream>

int main() {
    int balance = 0;
    int cmd = -1;

    do {
        std::cout << "1 - Add\n2 - Show\n3 - Reset\n0 - Exit\n";
        std::cin >> cmd;

        if (cmd == 2) {
            std::cout << "Balance = " << balance << '\n'; // Balance = 0
        }
    } while (cmd != 0);
}

Это ещё не “приложение мечты”, но уже видно важное: do-while делает структуру «показ меню → ввод команды → действие → повтор» очень прямой. С while это тоже можно сделать, но часто приходится либо дублировать вывод меню, либо городить подготовительные шаги перед циклом.

Добавление и сброс баланса

Теперь добавим обработку команд 1 и 3. Мы остаёмся в рамках уже изученного: только if/else и базовая арифметика. При этом важно не превращать тело цикла в “простыню”, где невозможно понять, что происходит. Лучше держать логику прямой: прочитали команду → выбрали действие → выполнили.

#include <iostream>

int main() {
    int balance = 0;
    int cmd = -1;

    do {
        std::cout << "1 - Add\n2 - Show\n3 - Reset\n0 - Exit\n";
        std::cin >> cmd;

        if (cmd == 1) {
            int add = 0;
            std::cout << "Add amount: ";
            std::cin >> add;
            balance = balance + add;
        } else if (cmd == 2) {
            std::cout << "Balance = " << balance << '\n';
        } else if (cmd == 3) {
            balance = 0;
            std::cout << "Balance reset.\n"; // Balance reset.
        }
    } while (cmd != 0);
}

Заметьте один важный момент: переменная add живёт только внутри ветки cmd == 1. Это нормально и даже полезно: меньше шансов случайно использовать её “потом”, когда она уже не нужна.

Обработка неизвестных команд

Если пользователь введёт 42, программа сейчас молча “проглотит” это и просто снова покажет меню. Формально это не ошибка, но UX получается как у автомата с кофе, который просто игнорирует вас и делает вид, что ничего не было (и всё же требует деньги). Давайте добавим мягкую обработку неизвестных команд.

#include <iostream>

int main() {
    int balance = 0;
    int cmd = -1;

    do {
        std::cout << "1 - Add\n2 - Show\n3 - Reset\n0 - Exit\n";
        std::cin >> cmd;

        if (cmd == 1) {
            int add = 0;
            std::cout << "Add amount: ";
            std::cin >> add;
            balance = balance + add;
        } else if (cmd == 2) {
            std::cout << "Balance = " << balance << '\n';
        } else if (cmd == 3) {
            balance = 0;
            std::cout << "Balance reset.\n";
        } else if (cmd != 0) {
            std::cout << "Unknown command.\n"; // Unknown command.
        }
    } while (cmd != 0);
}

Обратите внимание, как аккуратно выглядит условие else if (cmd != 0): мы не ругаемся на команду выхода, потому что она как раз правильная. Это мелочь, но из таких мелочей складывается “ощущение, что программа живая”.

4. Порядок выполнения и частые ловушки

Схема выполнения do-while

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

flowchart TD
    A[Вход в do-while] --> B[Выполнить тело цикла]
    B --> C{Условие истинно?}
    C -->|да| B
    C -->|нет| D[Выход из цикла]

Если вы будете держать в голове эту схему, исчезнет большая часть ошибок вида «почему оно выполнилось один раз, хотя условие ложное». Потому что для do-while это не баг, а смысл.

Цикл обязан двигаться к завершению

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

В нашем меню причина завершения простая: пользователь вводит 0, и условие cmd != 0 становится ложным. Если бы мы забыли читать cmd внутри тела (или случайно перетёрли cmd не тем значением), мы могли бы получить бесконечный цикл. Бесконечные циклы сами по себе не “зло”, но сегодня у нас фокус на корректных завершающихся циклах, поэтому относитесь к этому как к договору: «каждая итерация приближает нас к выходу».

Точка с запятой и пустое тело

У do-while есть две “классические ловушки”, которые встречаются так часто, что их можно считать народным фольклором программистов. Первая — забытая ; после while (...). Вторая — случайный ; после do { ... } while (...), который превращает цикл в странную конструкцию, где вы думаете, что внутри что-то происходит, а на самом деле нет.

Правильный шаблон стоит запомнить визуально:

do {
    // тело
} while (условие);
Шаблон цикла do-while с обязательной ;

Если вы однажды начнёте воспринимать } while (...) ; как одну “связку”, ошибок станет заметно меньше.

5. Типичные ошибки при работе с do-while

Ошибка №1: забыли точку с запятой после while (условие).
Эта ошибка особенно неприятна тем, что мозг кричит: «ну это же почти как while!», а компилятор кричит в ответ: «нет». Лечится просто: учите do-while как готовый шаблон do { ... } while (...); и в учебных проектах всегда ставьте фигурные скобки, даже если внутри одна строка.

Ошибка №2: перепутали условие продолжения и условие выхода.
Часто пишут условие “как выйти” и потом ставят его в while (...), получая цикл, который работает наоборот. Хорошая привычка — читать условие вслух именно как продолжение: «повторять, пока …». Если фраза звучит неестественно, значит вы, скорее всего, написали условие “на выход”, а не “на повтор”.

Ошибка №3: тело цикла не меняет ситуацию, и цикл становится бесконечным.
В меню это бывает, если вы забыли std::cin >> cmd;, или случайно читаете не в cmd, или постоянно присваиваете cmd одно и то же значение. В циклах ввода это бывает, если переменная в условии вообще нигде не меняется. Перед запуском программы полезно спросить себя: «Что изменится за одну итерацию? Почему это приближает нас к завершению?».

Ошибка №4: слишком много логики в одном месте, и цикл становится нечитаемым.
do-while часто используют для меню, а меню легко превращается в “комбайн”: десять команд, двадцать if, и вы уже боитесь открывать этот файл. Даже без функций (пока мы их не проходили) можно держать дисциплину: пусть тело делает одно и то же в одном и том же порядке — показать подсказку → прочитать команду → выполнить одну ветку → вернуться к началу.

Ошибка №5: случайно сделали пустой цикл из-за лишней ; не там.
Иногда ставят do { ... }; while (cond); или делают другие комбинации, после которых программа компилируется, но ведёт себя “как-то странно”. Когда поведение кажется магическим, почти всегда виновата пунктуация: ; и {}. В C++ это не украшения, а управляющие элементы языка — относитесь к ним как к проводам в щитке: лишний провод может не просто “мешать”, он может замкнуть логику.

1
Задача
C++ SELF, 4 уровень, 3 лекция
Недоступна
Первый “плюсовой” сигнал
Первый “плюсовой” сигнал
1
Задача
C++ SELF, 4 уровень, 3 лекция
Недоступна
Пароль на терминале
Пароль на терминале
1
Задача
C++ SELF, 4 уровень, 3 лекция
Недоступна
Консольный кликер
Консольный кликер
1
Задача
C++ SELF, 4 уровень, 3 лекция
Недоступна
Минимум и максимум до стоп-сигнала
Минимум и максимум до стоп-сигнала
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ