Powtarzanie bez licznika
Pętla for to właściwe narzędzie, gdy wiesz albo potrafisz obliczyć, ile przebiegów potrzebujesz. Wiele zadań nie przychodzi jednak z liczbą powtórzeń. Czytaj każdą linię pliku, aż się skończy. Pytaj o hasło, dopóki nie będzie poprawne. Dziel liczbę na pół, aż dojdzie do 1. W takich przypadkach pytanie nie brzmi "ile razy", tylko "do kiedy", i właśnie to wyraża while.
Nikt nie obliczył z góry "siedmiu". Pętla po prostu działa, dopóki warunek jest spełniony.
Składnia i zasada: najpierw warunek
while (warunek) {
// ciało: wykonuje się, dopóki warunek jest niezerowy
}
Warunek jest sprawdzany przed każdym przebiegiem, także pierwszym. Z tego wynikają trzy rzeczy:
- Pętla, której warunek na starcie jest fałszywy, wykonuje się zero razy.
while (0) { ... }nigdy nie wykona ciała. - To, co sprawdza warunek, musi być zainicjalizowane przed pętlą, inaczej sprawdzasz śmieciową wartość.
- Coś w ciele musi w końcu sprawić, że warunek stanie się fałszywy, inaczej pętla nigdy się nie skończy.
Podobnie jak w if, nawiasy są obowiązkowe i nie stawia się po nich średnika. Zbędny średnik to pętlowa wersja tego samego błędu:
/* BUG: ciałem jest pusta instrukcja, więc to kręci się w nieskończoność */
while (i < 10);
{
printf("%d\n", i);
i++;
}
i nigdy nie jest zwiększane, więc warunek się nie zmienia. gcc -Wall zgłasza puste ciało.
Zasada prawdziwości w C działa tu dokładnie tak samo jak w if/else: pętla trwa, dopóki wyrażenie jest niezerowe, i zatrzymuje się, gdy jest równe zero.
Standardowy kształt: inicjalizacja, test, aktualizacja
Prawie każda poprawna pętla while ma trzy elementy rozłożone w trzech miejscach:
To te same trzy części, które zawiera nagłówek for, tylko nie zebrane w jednej linii. I to jest szczera odpowiedź na pytanie "for czy while?": jeśli wszystkie trzy elementy dotyczą jednego licznika, for umieszcza je tam, gdzie czytający sprawdzi je razem. Jeśli aktualizacja jest rozrzucona po długim ciele albo warunek dotyczy czegoś innego niż liczba przebiegów, czytelniejszą formą jest while.
Zapomnienie o elemencie 3 to ten błąd pętli while. Jeśli pętla się zawiesza, najpierw sprawdź, czy ciało naprawdę zmienia to, co odczytuje warunek.
Pętle z wartownikiem
Wartownik (sentinel) to specjalna wartość oznaczająca "stop". Sumuj liczby, aż użytkownik wpisze 0:
Do 99 za 0 pętla nigdy nie dochodzi: wartownik ją zakończył. Zauważ, że liczba elementów nigdy nie jest znana z góry i właśnie dlatego jest to while.
Stringi w C działają tak samo: kończą się wartownikiem '\0', więc przejście po stringu to podręcznikowy while:
Czytanie wejścia do EOF
Najczęstszy while w prawdziwym kodzie czyta wejście, dopóki coś jest. Zasada brzmi: sprawdzaj wartość zwracaną przez funkcję wejścia, a nie wartość, którą zapisała.
getchar zwraca kolejny znak albo stałą EOF, gdy wejście się skończy:
Dwa ważne szczegóły. c jest typu int, bo EOF to wartość ujemna, która w wielu systemach nie mieści się w char; zadeklarowanie char może sprawić, że porównanie nigdy nie będzie prawdziwe. Poza tym przypisanie stoi wewnątrz warunku: (c = getchar()) != EOF odczytuje znak, zapisuje go i porównuje w jednym kroku. Wewnętrzne nawiasy są obowiązkowe, bo != wiąże mocniej niż =; bez nich w c trafiłby wynik porównania. To jedno z niewielu miejsc, gdzie przypisanie w warunku jest idiomem, a nie błędem.
Ten sam kształt działa ze scanf, która zwraca liczbę poprawnie przekonwertowanych elementów:
int n;
while (scanf("%d", &n) == 1) {
/* n zawiera poprawną liczbę */
}
Pętla kończy się na końcu pliku oraz na pierwszym wejściu, które nie jest liczbą, i zwykle właśnie o to chodzi.
Czego nie należy pisać, to while (!feof(f)). feof informuje, czy odczyt już się nie powiódł, więc gdy jesteś na ostatnim elemencie, wciąż zwraca fałsz, ciało pętli wykonuje się jeszcze raz, a odczyt zawodzi i przetwarzane są nieaktualne dane. Sprawdzaj sam odczyt.
while (1) i break
Czasem warunek wyjścia jest znany dopiero w środku ciała: po odczycie, ale przed przetworzeniem. Idiomatyczne rozwiązanie to celowo nieskończona pętla z jawnym wyjściem:
while (1) jest zawsze prawdziwe, więc jedyne wyjście to break (albo return, albo exit). Zobaczysz też for (;;) w tym samym celu; obie formy kompilują się do tego samego, a wybór między nimi w danym projekcie to kwestia stylu.
Stosuj ten kształt, gdy test wyjścia naprawdę należy do środka ciała. Gdy test naturalnie pasuje na początek, umieść go w warunku, gdzie czytający go zobaczy: while (1), którego jedynym break jest pierwsza instrukcja, to po prostu przebrany warunek.
Zamiana między for a while
Każdą pętlę for można zapisać jako while i odwrotnie. Przekształcenie jest mechaniczne:
/* for */ /* równoważny while */
for (init; cond; update) { init;
ciało; while (cond) {
} ciało;
update;
}
Te dwie pętle wypisują więc to samo:
Jedyne miejsce, gdzie ta równoważność przecieka, to continue. W pętli for continue skacze do aktualizacji w nagłówku, więc licznik i tak rośnie. W wersji while continue wraca prosto do warunku i pomija i++ na końcu ciała, co od razu daje nieskończoną pętlę:
/* BUG: continue pomija i++, więc pętla kręci się w nieskończoność na pierwszej liczbie parzystej */
int i = 0;
while (i < 10) {
if (i % 2 == 0) {
continue;
}
printf("%d ", i);
i++;
}
Jeśli pętla while używa continue, upewnij się, że aktualizacja następuje przed nim, albo użyj for, gdzie gwarantuje ją nagłówek.
Częste pułapki
- Nic nie aktualizuje warunku. Ciało musi zmieniać to, co odczytuje warunek. Jeśli program się zawiesza, zajrzyj najpierw tutaj.
- Średnik po nagłówku.
while (cond);sprawia, że ciałem jest pusta instrukcja. - Sprawdzanie zmiennej, której nigdy nie zainicjalizowano. Warunek wykonuje się przed ciałem, więc odczytywana wartość musi już istnieć.
charzamiastintdlagetchar.EOFnie mieści się wchar; porównanie może wtedy nigdy nie być prawdziwe albo zwykły bajt zostanie wzięty zaEOF.while (!feof(f)). Pętla wykonuje się o jeden raz za dużo. Sprawdzaj odczyt.- Założenie, że ciało wykona się co najmniej raz. Nie wykona się. Gdy potrzebujesz jednego gwarantowanego przebiegu, na przykład wypisania menu przed sprawdzeniem wyboru, użyj pętli do-while.
Najczęściej zadawane pytania
Jak napisać pętlę while w C?
while (warunek) { ciało }. Warunek jest sprawdzany przed każdym przebiegiem, więc ciało wykonuje się tylko wtedy, gdy warunek jest niezerowy, a jeśli od początku jest fałszywy, nie wykona się ani razu. Coś w ciele pętli musi w końcu zmienić warunek, inaczej pętla nigdy się nie skończy.
Czym różni się for od while w C?
Mają taką samą siłę wyrazu: każdą pętlę for można przepisać jako while. Używaj for, gdy licznik, jego limit i krok należą razem do jednego nagłówka, a while, gdy warunek zatrzymania nie jest liczbą przebiegów: czytanie do końca pliku, ponawianie aż do sukcesu albo pętla do zmiany flagi.
Jak wczytywać dane do EOF w C?
Sprawdzaj wartość zwracaną przez funkcję wejścia, a nie odczytaną wartość: while (scanf("%d", &n) == 1) { ... } zatrzymuje się na końcu pliku albo na pierwszym nienumerycznym wejściu, a while ((c = getchar()) != EOF) { ... } czyta znak po znaku. Nigdy nie pisz pętli while (!feof(f)): wykonuje się o jeden raz za dużo, już za końcem danych.
Dlaczego moja pętla while w C jest nieskończona?
Zwykle dlatego, że nic w ciele pętli nie zmienia wartości sprawdzanej w warunku, albo dlatego, że zbędny średnik po nagłówku (while (i < 10);) sprawił, że ciałem jest pusta instrukcja. Obie wersje kompilują się bez problemu, więc sprawdź, czy ciało naprawdę aktualizuje zmienną z warunku.