Dlaczego pętla for
switch wybiera jedną gałąź i wykonuje ją raz. Prawdziwe programy muszą jednak robić coś wielokrotnie: wypisać każdy wynik, zsumować listę liczb, narysować 10 wierszy siatki. Pętla for to w C++ koń pociągowy do powtarzania kodu znaną liczbę razy, z wbudowanym licznikiem, nad którym masz kontrolę.
Wszystko, czego potrzebuje pętla for, mieści się w jednym zwartym nagłówku, więc cała historia "ile razy i jak" jest widoczna na pierwszy rzut oka.
Trzyczęściowy nagłówek
Nagłówek pętli for ma trzy części oddzielone średnikami: inicjalizację, warunek i aktualizację.
for (initializer; condition; update) {
// ciało: wykonuje się, dopóki warunek jest prawdziwy
}
Wykonują się w określonej kolejności: inicjalizacja raz na początku; potem przed każdą iteracją sprawdzany jest warunek; ciało wykonuje się tylko wtedy, gdy warunek jest true; a aktualizacja wykonuje się na końcu każdej iteracji, tuż przed ponownym sprawdzeniem warunku.
Tutaj int i = 0 wykonuje się raz. Potem sprawdzane jest i < 5: dopóki jest spełnione, ciało wypisuje tekst, a i++ zwiększa licznik. Gdy i osiąga 5, warunek jest false, więc pętla się kończy i wypisuje się done. Ciało wykonuje się dokładnie 5 razy, a i przyjmuje wartości od 0 do 4.
Zadeklarowanie licznika w nagłówku (int i = 0) ogranicza zasięg i do pętli: nie istnieje on po zamykającej klamrze, i dokładnie tego chcesz.
Liczenie w górę, w dół i z krokiem
Część aktualizująca nie ogranicza się do i++. Możesz liczyć w dół, przesuwać się o dowolny krok albo iterować po indeksie tablicy.
Pierwsza pętla działa, dopóki i > 0, za każdym razem zmniejszając licznik, więc wypisuje 5 4 3 2 1. Druga dodaje 2 w każdym przebiegu i używa <= 10, bo 10 to wartość, którą chcemy uwzględnić. Dopasuj warunek do aktualizacji: liczenie w dół łączy się z > lub >=, a liczenie w górę z < lub <=.
Pętla po tablicy
Najczęstsze zastosowanie pętli licznikowej to przechodzenie tablicy po indeksie. Licznik służy jednocześnie za pozycję, którą odczytujesz.
Zwróć uwagę, że warunek to i < n, a nie i <= n. Tablica 5-elementowa ma poprawne indeksy od 0 do 4; indeks 5 leży za końcem. Odczyt scores[5] to niezdefiniowane zachowanie: może wypisać śmieci, wywołać awarię albo pozornie działać i po cichu uszkodzić pamięć. Wzorzec i < n to bezpieczny wybór domyślny dla każdej tablicy indeksowanej od zera.
Jeśli potrzebujesz tylko wartości, a nie indeksu, czystsza jest pętla for po zakresie. Po klasyczną pętlę z indeksem sięgaj wtedy, gdy naprawdę potrzebujesz pozycji.
break i continue
Dwa słowa kluczowe pozwalają zmienić przebieg w środku pętli. break natychmiast wychodzi z pętli; continue pomija resztę bieżącej iteracji i przeskakuje do aktualizacji.
Pierwsza pętla zatrzymuje się w chwili znalezienia 7 i nigdy nie sprawdza reszty. Druga używa continue, aby pominąć wypisywanie w ciele, gdy i jest parzyste; aktualizacja i++ nadal się wykonuje, więc pętla idzie dalej. Subtelna pułapka: continue przeskakuje do aktualizacji, więc jeśli w pętli, której licznik jest aktualizowany w ciele, a nie w nagłówku, polegasz na continue, możesz przypadkiem pominąć tę aktualizację i kręcić się w nieskończoność.
Pętle zagnieżdżone
Umieść jeden for w drugim, aby pracować z siatkami, tabelami albo parami. Pętla wewnętrzna wykonuje się w całości dla każdego pojedynczego kroku pętli zewnętrznej.
To wypisuje siatkę tabliczki mnożenia 3x3. Pętla zewnętrzna ustala row, pętla wewnętrzna przechodzi każde col dla tego wiersza, a potem znak nowej linii kończy wiersz. Nadawaj licznikom różne nazwy (row/col, a nie i/i): ponowne użycie jednej nazwy przesłania zewnętrzny licznik i prowadzi do zagadkowych błędów. Uważaj też na koszt: zagnieżdżenie pętli o n krokach w innej pętli o n krokach wykonuje ciało n * n razy, a to szybko rośnie.
Częste pułapki
Kilka pułapek odpowiada za większość błędów z pętlą for w C++:
- Błąd o jeden:
i <= nprzy rozmiarze liczonym od zera odczytuje jeden element za końcem. Używaji < n. - Niedomiar liczb bez znaku: licznik typu bez znaku odliczany w dół nigdy nie spadnie poniżej zera.
for (size_t i = n - 1; i >= 0; i--)kręci się w nieskończoność, boi >= 0jest zawsze prawdziwe dla wartości bez znaku: gdyiwynosi0,i--zawija się do ogromnej liczby dodatniej. Do odliczania w dół używajintze znakiem albo przepisz warunek. - Zmienianie licznika w ciele: modyfikowanie
izarówno w ciele, jak i w nagłówku sprawia, że liczba iteracji staje się nieprzewidywalna. Wybierz jedno miejsce.
// BUG: infinite loop - unsigned i is never < 0
for (size_t i = n - 1; i >= 0; i--) {
process(arr[i]);
}
Kolejnym cichym zagrożeniem są liczniki zmiennoprzecinkowe: for (double x = 0.0; x != 1.0; x += 0.1) może nigdy nie trafić dokładnie w 1.0, bo 0.1 nie da się zapisać precyzyjnie. Iteruj z licznikiem całkowitym i obliczaj wartość w środku albo użyj < zamiast !=.
Dalej: pętle while
Pętla for błyszczy, gdy liczbę powtórzeń znasz z góry. Czasem jednak trzeba powtarzać do momentu zmiany warunku (czytać dane aż do końca pliku, ponawiać próbę aż do sukcesu) bez ustalonej liczby kroków. To zadanie pętli while, której nagłówek sprowadza się do samego warunku. O niej jest następna strona.
Najczęściej zadawane pytania
Jak napisać pętlę for w C++?
Umieść w nagłówku trzy części oddzielone średnikami: inicjalizację, warunek i aktualizację. for (int i = 0; i < 5; i++) { cout << i; } wykonuje ciało, gdy i przyjmuje kolejno wartości 0, 1, 2, 3, 4. Pętla kończy się, gdy tylko warunek stanie się false.
Czym różni się pętla for od pętli for po zakresie w C++?
Klasyczny for daje ci licznik indeksu, nad którym masz kontrolę (for (int i = 0; i < n; i++)), potrzebny, gdy chcesz znać pozycję albo przesuwać się w niestandardowy sposób. Pętla for po zakresie (for (int x : v)) ukrywa indeks i po prostu podaje ci każdy element: to czystsze rozwiązanie, gdy potrzebujesz tylko wartości.
Dlaczego moja pętla for w C++ wykonuje się o jeden raz za dużo lub za mało?
To klasyczny błąd o jeden (off-by-one). Użycie <= zamiast < przy rozmiarze liczonym od zera wykonuje jedną dodatkową iterację i czyta za końcem tablicy; użycie <, gdy ostatnia wartość miała być uwzględniona, daje o jedną iterację za mało. Dla tablicy o rozmiarze n bezpieczny wzorzec to for (int i = 0; i < n; i++).