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 << "Привіт!\n"; // Привіт!
    } 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 << "Введіть 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 - Додати\n2 - Показати\n3 - Скинути\n0 - Вихід\n";
        std::cin >> cmd;

        if (cmd == 2) {
            std::cout << "Баланс = " << balance << '\n'; // Баланс = 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 - Додати\n2 - Показати\n3 - Скинути\n0 - Вихід\n";
        std::cin >> cmd;

        if (cmd == 1) {
            int add = 0;
            std::cout << "Сума поповнення: ";
            std::cin >> add;
            balance = balance + add;
        } else if (cmd == 2) {
            std::cout << "Баланс = " << balance << '\n';
        } else if (cmd == 3) {
            balance = 0;
            std::cout << "Баланс скинуто.\n"; // Баланс скинуто.
        }
    } while (cmd != 0);
}

Зауважте один важливий момент: змінна add існує лише всередині гілки cmd == 1. Це нормально й навіть корисно: менше шансів випадково використати її «пізніше», коли вона вже не потрібна.

Обробка невідомих команд

Якщо користувач введе 42, програма зараз мовчки це «проковтне» й просто знову покаже меню. Формально це не помилка, але UX виходить як в автомата з кавою, який просто ігнорує вас і робить вигляд, що нічого не сталося. І все одно просить гроші. Додаймо м’яку обробку невідомих команд.

#include <iostream>

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

    do {
        std::cout << "1 - Додати\n2 - Показати\n3 - Скинути\n0 - Вихід\n";
        std::cin >> cmd;

        if (cmd == 1) {
            int add = 0;
            std::cout << "Сума поповнення: ";
            std::cin >> add;
            balance = balance + add;
        } else if (cmd == 2) {
            std::cout << "Баланс = " << balance << '\n';
        } else if (cmd == 3) {
            balance = 0;
            std::cout << "Баланс скинуто.\n";
        } else if (cmd != 0) {
            std::cout << "Невідома команда.\n"; // Невідома команда.
        }
    } 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 з обов’язковою ;

Якщо ви одного разу почнете сприймати } 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++ це не прикраси, а керувальні елементи мови. Ставтеся до них як до дротів у щитку: зайвий дріт може не просто «заважати», а й замкнути логіку.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ