Menu

Pętla do-while w C: składnia, przykłady i kiedy jej używać

Pętla w C, która najpierw wykonuje ciało: jak do-while gwarantuje jeden obieg przed sprawdzeniem warunku, dlaczego kończy się średnikiem, wzorce menu i ponawiania, do których ją stworzono, i czym różni się od zwykłej pętli while.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Pętla, która zawsze wykonuje się raz

Pętla while sprawdza, zanim zadziała, więc może wykonać się zero razy. Zwykle tak właśnie powinno być. Czasem jednak to dokładnie odwrotność tego, czego trzeba: nie wiesz, czy powtórzyć pytanie, dopóki go nie pokażesz, i nie ocenisz wejścia użytkownika, dopóki czegoś nie wczytasz. do-while przenosi test na koniec, więc ciało wykonuje się najpierw, a warunek decyduje tylko o tym, czy wykonać je jeszcze raz.

Warunek i < 5 jest fałszywy od początku. Pętla while wykonuje się zero razy; do-while dokładnie raz. To cała różnica między nimi.

Składnia i ten średnik

do {
    // ciało: zawsze wykonuje się co najmniej raz
} while (warunek);

Kolejność czyta się tak, jak przebiega wykonanie: wykonaj ciało, a dopóki warunek jest spełniony, wykonaj je znowu.

Średnik po nawiasie zamykającym jest obowiązkowy. do ... while (cond); to jedna instrukcja, a instrukcje w C kończą się średnikiem. Pomiń go, a kompilator będzie czytał dalej i zgłosi błąd składni gdzieś niżej, dlatego prawdziwa pomyłka często jest kilka linii od tej, którą wskazuje komunikat.

To także jedyne miejsce w C, gdzie while (cond); ze średnikiem jest poprawne, a nie jest błędem. W zwykłej pętli while ta sama interpunkcja tworzy puste ciało i zwykle nieskończoną pętlę; tutaj zamyka do.

Nawiasy klamrowe są opcjonalne przy ciele z jedną instrukcją, ale jak w każdej innej strukturze sterującej w C i tak je pisz.

Wzorzec walidacji wejścia

Do tego właśnie zaprojektowano do-while. Musisz coś wczytać, zanim ocenisz, czy to jest do przyjęcia, więc odczyt musi nastąpić przed testem:

Pętla wczytuje dalej, aż wartość przejdzie test, a ten sam warunek, który odrzuca złą wartość, prosi o kolejny obieg. Zapisany jako while, odczyt musiałby pojawić się dwa razy: raz przed pętlą, żeby przygotować warunek, i raz na końcu ciała. Właśnie to powtórzenie usuwa do-while.

Przy prawdziwym wejściu ciało wywoływałoby scanf:

int age;
do {
    printf("Podaj swoj wiek (1-120): ");
    if (scanf("%d", &age) != 1) {
        while (getchar() != '\n') { }   /* odrzuć złą linię */
        age = -1;                        /* wymuś kolejny obieg */
    }
} while (age < 1 || age > 120);

Zwróć uwagę na wewnętrzną pętlę while, która opróżnia resztę linii. Jeśli scanf nie zdoła przekonwertować danych, problematyczny tekst nadal siedzi w buforze wejścia i każdy kolejny odczyt też się na nim wywraca: pętla kręci się w nieskończoność, wypisując pytanie. Wyczyszczenie bufora jest częścią wzorca.

Wzorzec menu

Drugie klasyczne zastosowanie: pokaż opcje, wczytaj wybór, wykonaj go i powtarzaj, dopóki użytkownik nie wyjdzie. Menu musi pojawić się, zanim będzie jakikolwiek wybór do sprawdzenia.

break wewnątrz switch kończy switch, a nie pętlę: warto być tu precyzyjnym, bo oba używają tego samego słowa kluczowego. Tutaj pętla kończy się, bo choice != 4 staje się fałszem. Żeby naprawdę opuścić pętlę z wnętrza switch, trzeba użyć return albo zmiennej-flagi; sam break tego nie zrobi.

Gwarantowany pierwszy obieg może być błędem

Ponieważ ciało wykonuje się przed jakimkolwiek testem, do-while chętnie przetworzy dane, które nie istnieją:

/* BUG: wypisuje a[0] nawet wtedy, gdy n wynosi 0 */
int i = 0;
do {
    printf("%d\n", a[i]);
    i++;
} while (i < n);

Przy n == 0 to czyta a[0] z pustej tablicy: niezdefiniowane zachowanie. Każda pętla po kolekcji, która może być pusta, potrzebuje while albo for, gdzie test jest na początku.

Zasada ogólna: używaj do-while tylko wtedy, gdy wykonanie zero razy jest niemożliwe z natury problemu, a nie tylko mało prawdopodobne w praktyce. Pytanie użytkownika o dane, rzucanie kostką, aż wypadnie docelowa liczba, generowanie i sprawdzanie kandydata: tych rzeczy naprawdę nie da się ocenić przed pierwszym obiegiem. Przechodzenie po liście nie może tego o sobie powiedzieć.

break i continue w do-while

Oba działają, a continue ma haczyk, który warto znać: skacze do sprawdzenia warunku, a nie na początek ciała.

i++ na początku ciała wykonuje się w każdym obiegu, również w tych przerwanych przez continue, więc pętla dalej postępuje. Umieść aktualizację za continue, a pętla się zawiesi: to ta sama pułapka, którą mają pętle while.

break natychmiast opuszcza pętlę i całkowicie pomija warunek.

Wybór spośród trzech pętli

Trzy pętle w C mają równe możliwości; wybór dotyczy tego, która najlepiej wyraża intencję.

for        licznik, jego limit i krok należą do siebie: "n razy"
while      powtarzaj, aż zmieni się warunek, może ani razu: "dopóki"
do-while   to samo, ale pierwszy obieg jest gwarantowany: "co najmniej raz"

W praktyce do-while jest zdecydowanie najrzadziej używaną z trzech. Większość pętli albo liczy, albo musi obsłużyć przypadek pusty, a w obu sytuacjach test powinien być na górze. Sięgaj po do-while, gdy przyłapiesz się na powielaniu instrukcji tuż przed pętlą while tylko po to, żeby przygotować jej warunek: to powtórzenie jest sygnałem.

Najczęściej zadawane pytania

Jak napisać pętlę do-while w C?

do { ciało } while (warunek);: najpierw ciało, potem test, a całą instrukcję kończy średnik. Ciało zawsze wykonuje się co najmniej raz, a potem powtarza się, dopóki warunek jest niezerowy.

Jaka jest różnica między while a do-while w C?

Miejsce, w którym stoi test. Pętla while sprawdza warunek przed pierwszym obiegiem, więc może wykonać się zero razy. do-while sprawdza go po obiegu, więc zawsze wykonuje się co najmniej raz. Wybieraj do-while tylko wtedy, gdy ten gwarantowany pierwszy obieg jest naprawdę tym, czego wymaga problem.

Dlaczego pętla do-while wymaga średnika na końcu?

Bo do ... while (cond); to jedna instrukcja, a jak każda inna instrukcja w C kończy się średnikiem. Jego pominięcie daje mylący błąd składni wskazujący na następną linię, bo kompilator czyta dalej w poszukiwaniu reszty instrukcji.

Kiedy używać pętli do-while?

Gdy ciało musi się wykonać, zanim w ogóle da się stwierdzić, czy powtarzać: pokazanie menu przed odczytaniem wyboru, ponowne pytanie aż do uzyskania poprawnego wejścia albo wygenerowanie wartości i jej sprawdzenie. Jeśli warunek można sprawdzić z góry, pętla while wyraża to prościej.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ