Menu

break i continue w C: jak działają i kiedy ich używać

break opuszcza pętlę albo switch; continue przechodzi do następnego obiegu. Jak każde z nich zachowuje się w for, while i do-while, jak wyjść z zagnieżdżonych pętli za pomocą flagi albo goto i jakie nadużycia sprawiają, że pętle stają się trudne do śledzenia.

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

Dwa sposoby na zmianę przebiegu pętli

Warunek pętli decyduje, kiedy się ona kończy, ale czasem prawdziwa odpowiedź pojawia się w środku ciała. Przeszukujesz tablicę i znajdujesz element na trzeciej pozycji: po co sprawdzać pozostałe 997? Albo przetwarzasz listę i jeden wpis jest pusty: pomiń go i idź dalej.

C ma po jednym słowie kluczowym na każdy przypadek:

  • break: opuść pętlę teraz. Wykonanie wraca do pierwszej instrukcji za nią.
  • continue: porzuć tylko ten obieg. Przejdź do następnego.

Pierwsza pętla wypisuje 1 2 3 4 i się zatrzymuje. Druga wypisuje 1 2 3 4 6 7 8 9 10: pominęła jedną wartość i poszła dalej.

break w pętli

Kanoniczne zastosowanie to wyszukiwanie, które kończy się zaraz po sukcesie:

Ponieważ found jest ustawiane przed break, kod za pętlą potrafi odróżnić "znaleziono" od "skończyły się elementy". Taka para, czyli zmienna na wynik plus break, to standardowy idiom wyszukiwania w C. Sam licznik pętli nie nadaje się do użycia później, gdy jest zadeklarowany w nagłówku, bo wychodzi z zasięgu przy nawiasie zamykającym.

break zawsze opuszcza jedną pętlę: najbliższe otaczające for, while, do-while albo switch.

continue w pętli

continue służy do wpisów, które należy pominąć, zamiast przerywać pracę:

To, dokąd skacze continue, zależy od pętli:

for        -> do części aktualizującej nagłówka (licznik i tak się zwiększa)
while      -> do sprawdzenia warunku (nic więcej z ciała się nie wykona)
do-while   -> do sprawdzenia warunku na dole

Ta różnica nie jest akademicka. W pętli for instrukcja continue nigdy nie pominie inkrementacji licznika, bo inkrementacja jest w nagłówku. W pętli while może:

/* BUG: continue pomija i++, więc to zawiesza się na pierwszej liczbie parzystej */
int i = 0;
while (i < 10) {
    if (i % 2 == 0) {
        continue;
    }
    printf("%d ", i);
    i++;
}

Przenieś i++ nad continue albo zapisz to jako pętlę for, w której nagłówek nie pozwala się pomylić.

break w switch to nie break w pętli

break kończy najbliższą otaczającą pętlę albo switch, zależnie od tego, co jest bliżej. Umieść switch w pętli, a każdy break w nim należy do switch:

Polecenie q wypisuje swój komunikat, a pętla dalej przechodzi przez cc. Jeśli wyjście miało zakończyć pętlę, ten break tego nie zrobił. Rozwiązania to flaga, goto albo przeniesienie pętli do funkcji i użycie return; wszystkie pokazane są niżej.

Wychodzenie z zagnieżdżonych pętli

Pojedynczy break opuszcza jeden poziom. W dwóch zagnieżdżonych pętlach kończy tylko wewnętrzną, a zewnętrzna zaczyna kolejny obieg, jakby nic się nie stało.

C nie ma break 2 ani break z etykietą. Są trzy uczciwe opcje:

Flaga. Przenośna, oczywista i trochę gadatliwa:

Flaga pojawia się w dwóch miejscach, w zewnętrznym warunku i w przypisaniu, i to jest jej koszt. Przy trzech poziomach zagnieżdżenia robi się naprawdę nieprzyjemnie.

goto. Mocno oczerniany skok w C, a to jedyny przypadek, w którym doświadczeni programiści C używają go bez przepraszania:

goto w przód do etykiety tuż za pętlami robi dokładnie to, co break z etykietą w innych językach, w jednej linii i bez flagi, którą trzeba utrzymywać w zgodzie. Złą reputację goto zawdzięcza skokom wstecz i skokom do środka innych bloków, które rzeczywiście dają nieczytelny przepływ sterowania. Pojedynczy skok w przód z zagnieżdżonych pętli to nie to samo. Jądro Linuksa używa tego samego wzorca w ścieżkach sprzątania. Używaj go świadomie i oszczędnie; nie pozwól, żeby tabu zepchnęło cię do rozwiązania z trzema flagami, które czyta się trudniej.

Funkcja i return. Zwykle najlepsza odpowiedź, bo wyszukiwanie zasługuje na nazwę:

return zamyka wszystkie pętle w funkcji, nie potrzebuje flagi ani etykiety, a kod wyszukiwania ma teraz nazwę i można go testować osobno. Gdy zagnieżdżenie jest na tyle głębokie, że wyjście z niego sprawia kłopot, często jest to wskazówka, że ten blok i tak chciał być funkcją.

Kiedy pomagają, a kiedy szkodzą

break i continue zasługują na swoje miejsce, gdy pozwalają pętli powiedzieć "ten przypadek jest załatwiony" raz, blisko początku, zamiast owijać całe ciało w if. Porównaj:

/* z continue: właściwa praca nie jest wcięta */
for (int i = 0; i < n; i++) {
    if (!isValid(a[i])) continue;
    if (isDuplicate(a[i])) continue;
    process(a[i]);
}

/* bez niego: każdy warunek ochronny dodaje poziom */
for (int i = 0; i < n; i++) {
    if (isValid(a[i])) {
        if (!isDuplicate(a[i])) {
            process(a[i]);
        }
    }
}

Szkodzą, gdy jest ich dużo. Pętla z pięcioma rozrzuconymi break i trzema continue ma warunek wyjścia, który nigdzie nie jest zapisany: czytelnik musi przesymulować całe ciało, żeby wiedzieć, kiedy się zatrzyma. Dwie zasady, które dobrze się sprawdzają: trzymaj warunki z continue razem na początku ciała, a jeśli pętla ma więcej niż jeden czy dwa break, zastanów się, czy tej pracy nie powinien wykonywać warunek w nagłówku.

Dwie mniejsze pułapki. continue w do-while skacze do sprawdzenia na dole, które i tak jest następne: nieszkodliwe, ale zaskakuje. A break poza jakąkolwiek pętlą czy switch to błąd kompilacji, czyli jedyna pomyłka z tej strony, którą kompilator wyłapie za ciebie.

Najczęściej zadawane pytania

Jaka jest różnica między break a continue w C?

break całkowicie kończy pętlę i wykonanie wraca za nią. continue porzuca tylko bieżący obieg i przechodzi do następnego: w pętli for do części aktualizującej, a w while i do-while od razu do sprawdzenia warunku.

Jak wyjść z zagnieżdżonej pętli w C?

Pojedynczy break opuszcza tylko najbardziej wewnętrzną pętlę. Żeby wyjść z obu, ustaw flagę i sprawdzaj ją w warunku pętli zewnętrznej, użyj goto, by skoczyć do etykiety za pętlami, albo, co zwykle jest najczystsze, przenieś pętle do funkcji i użyj return.

Czy break wychodzi z pętli, czy tylko ze switch?

Z tego, co otacza go najbliżej. break wewnątrz switch, który jest w pętli, kończy tylko switch; pętla działa dalej. Żeby opuścić pętlę z wnętrza switch, potrzebujesz flagi, goto albo return.

Dlaczego continue powoduje nieskończoną pętlę while?

Bo continue skacze do warunku i pomija resztę ciała, łącznie z inkrementacją licznika, jeśli stoi na końcu. W pętli for aktualizacja jest w nagłówku, więc wykonuje się zawsze; w pętli while przenieś aktualizację przed continue albo użyj for.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ