Ten artykuł nie jest skończony. Możesz pomóc w jego ukończeniu edytując tą stronę.
Pętle
W tej lekcji każemy programowi wykonywać wielokrotnie dany kod, czyli skorzystamy z pętli.
Motywacja
Pętle mają wiele zastosowań, oto kilka z nich:
- 👾 dodanie np. 10 nowych przeciwników do planszy w grze
- 🖥 wyświetlenie każdego elementu z tablicy
- ➗ wielokrotne wykonanie obliczeń (np. liczenie silni, ciąg fibonacciego)
W lekcji o wektorach pokazaliśmy już jedną pętlę, która wyświetlała wszystkie elementy tablicy:
for (int n : numbers)
{
std::cout << n << ' ';
}
Jest to najprostsza wersja pętli w C++. W następnych sekcjach poznasz więcej ich rodzajów.
Rodzaje pętli
W C++ mamy następujące pętle:
for- wersja dla zasięgów (range-based for)
- wersja podstawowa
whiledo ... while
Najczęściej używana jest for oraz while i o nich powiemy w tej lekcji.
Jeśli chcesz poczytać o pętli do ... while to zapoznaj się z artykułem:
Pętla do ... while.
Iteracja - pojedynczy obieg pętli.
Wytłumaczenie
Pętla for (range-based)
Ten rodzaj pętli jest najczęściej stosowany do pracy z tablicami, choć może być użyty też w inny sposób.

W przykładzie pokazanym w sekcji Motywacja znajduje się właśnie
pętla range-based for, czyli wersja pętli for dla tzw. zasięgów.
Tablica w rozumieniu C++ również jest takim zasięgiem, więc możemy śmiało z niej skorzystać.
Najprostszy przykład:
std::vector<int> numbers = { 13, 42, -1, 0, -3, -5 };
for (int n : numbers)
{
std::cout << n << ' ';
}
Ta pętla kolejno przechodzi przez każdy element tablicy numbers i zapisuje go do
zmiennej n. Następnie wykonywany jest blok kodu zawarty w nawiasie klamrowym.
W tym wypadku jest to wyświetlenie liczby.
Zwróć uwagę, że po nazwie zmiennej n znajduje się dwukropek (:), nie średnik (;)!
Nie używamy w tym zapisie żadnego znaku równości (=), bo wartość każdego elementu po
kolei będzie automatycznie przypisywana do n.
Pętla while

Celowo przechodzimy teraz do pętli while, zamiast do zwykłego for, ponieważ ułatwi to wyjaśnienia.
Pętla while wykonuje ciało pętli dopóki warunek jest spełniony:
int number = 0;
while (number <= 3)
{
std::cout << number << ' ';
number++;
}
0 1 2 3
Warunek zostanie sprawdzony przed każdym obiegiem pętli i tak długo jak jest on spełniony,
czyli w tym wypadku tak długo jak number jest mniejsza lub równa 3, to będzie wykonywane ciało:
- wyświetlenie
number - zwiększenie
numbero 1
Po ostatnim obiegu pętli, wartość number będzie równa 4, więc warunek nie będzie spełniony,
przez co pętla się zakończy i komputer przejdzie do wykonywania następnych instrukcji.
Pętla for

Ta pętla jest uproszczeniem pewnego bardzo często powtarzającego się schematu i jest ona zazwyczaj używana do krokowego przejścia przez pewien zakres (np. liczbowy).
Zacznijmy od przykładu:
for (int i = 0; i < 10; i++)
{
std::cout << i << ' ';
}
Powyższa pętla wyświetla liczby od 0 do 9. Nawias okrągły przy for składa się z trzech części,
oddzielonych średnikami:
| Fragment | Opis |
|---|---|
int i = 0 | instrukcja początkowa (zazwyczaj utworzenie zmiennej) |
i < 10 | warunek |
i++ | wyrażenie iteracji |
Gdy program zaczyna wykonywać pętlę for, jednorazowo wykonuje instrukcję początkową - w naszym wypadku
tworzy zmienną i nadaje jej wartość 0.
Program następnie:
- sprawdzi warunek
- niespełniony: wyjdź z pętli
- spełniony: idź do punktu 2
- wykona ciało pętli
- wykona wyrażenie iteracji i przejdzie do pkt. 1
Powyższa pętla for jest równoznaczna z:
int i = 0;
while (i < 10)
{
std::cout << i << ' ';
i++;
}
Iteracja po tablicach
Pętli for bardzo często używamy do iterowania po tablicach, w sytuacji gdy
albo potrzebujemy mieć dostęp do numeru iteracji lub gdy nie chcemy iterować
po całym zakresie.
std::vector<int> numbers = {10, 13, 15, 18, 60};
for (int i = 0; i < numbers.size(); i++)
{
std::cout << "numbers[" << i << "]: " << numbers[i] << '\n';
}
std::vector<int> numbers = {10, 13, 15, 18, 60};
for (int i = 0; i < numbers.size() / 2; i++)
{
std::cout << "numbers[" << i << "]: " << numbers[i] << '\n';
}
Pusty nawias
Kod podawany w nawiasie pętli for jest opcjonalny. Średniki są wymagane.
for ( ; ; )
{
// kod
}
Powyższy zapis sprawi, że pętla for będzie wykonywała się w nieskończoność (ze względu na pusty warunek),
chyba że przerwiemy ją manualnie...
Przerwanie pętli
Pętlę możemy przerwać w dowolnym momencie za pomocą instrukcji break:
for (int i = 0; i < 10; i++)
{
if (i == 5)
break;
std::cout << i << ' ';
}
Ta pętla wyświetli liczby od 0 do 4, ponieważ przy i równym 5 wykonanie pętli zostanie przerwane.
W ten sam sposób możemy przerwać pętlę while.
Przerwanie obiegu pętli
Aby pominąć wykonywanie aktualnego obiegu pętli używamy instrukcji continue:
for (int i = 0; i < 10; i++)
{
if (i == 5)
continue;
std::cout << i << ' ';
}
Pętla wyświetli liczby od 0 do 9 z pominięciem liczby 5, bo zanim wykona instrukcję
wyświetlania (std::cout) to program przeskoczy do następnego obiegu.
Zauważ, że użycie continue w pętli for nie pomija wyrażenia iteracji (zobacz schemat powyżej).
Przykłady
Ta sekcja wymaga rozbudowy. Możesz nam pomóc edytując tą stronę.
Potencjalne błędy
Próba użycia zmiennej zadeklarowanej w for poza nią
Instrukcja początkowa w pętli for najczęściej służy do zadeklarowania zmiennej iteracyjnej.
Czasem się zdarza, że chcemy użyć tej zmiennej poza ciałem instrukcji for.
Nie jest to możliwe, ponieważ zmienna ta jest dostępna tylko i wyłącznie wewnątrz ciała tej zmiennej.
- ✔ OK
- ✔ OK
- ❌ Źle
#include <iostream>
int main()
{
for(int i = 0; i < 5; i++)
std::cout << i; // 🟢 Ok, zmienna użyta wewnątrz pętli
}
#include <iostream>
int main()
{
int sum = 0;
for(int i = 0; i < 5; i++)
{
sum += i; // 🟢 Ok, zmienna użyta wewnątrz pętli
}
std::cout << sum << '\n';
}
#include <iostream>
int main()
{
for(int i = 0; i < 5; i++)
std::cout << i;
std::cout << i; // 🔴 Nie ok, zmienna użyta poza pętlą
}
W przypadku niepoprawnego kodu wyżej, możemy spotkać się z nastepującymi błędami:
Niepoprawny warunek w for
Jednym z najczęstszych błędow logicznych który zdarza się podczas używania pętli for to zapisanie złego warunku pętli.
Warunek pętli to wyrażenie, które decyduje o dalszym przebiegu pętli (lub w ogóle zaczęciu przebiegu pętli), więc jeśli zapiszemy niepoprawny warunek, nasza pętla może lecieć w nieskończoność, nigdy się nie zacząć, lub wykonać się niepoprawną liczbę razy.
Należy więc dokładnie analizować warunki w bardziej skomplikowanych pętlach.
Iteracja od tyłu
Jeśli używamy auto lub std::size_t jako typ zmiennej iteracyjnej i chcemy iterować od N do 0, możemy się spotkać z przykrą niespodzianką.
Rozważmy przykładowy kod wypisujący liczby z tablicy od tyłu:
#include <iostream>
#include <vector>
int main()
{
std::vector<int> numbers = { 5, 4, 3, 2, 1 };
std::cout << "Liczby wypisane od tyłu: ";
for(auto i = numbers.size() - 1; i >= 0; i--)
{
std::cout << numbers[i] << ' ';
}
}
Kiedy uruchomimy ten program, wpadnie on w nieskończoną pętlę.
Jak na razie nie będziemy omawiać szczegółów tego problemu, jednak zapamiętajmy, że zamiast auto można w tym przypadku użyć int i to rozwiąże problem:
// ...
for(int i = numbers.size() - 1; i >= 0; i--)
// ...
Bardzo możliwe, że kompilator pokaże nam w tym przypadku ostrzeżenie, jednak nie należy się tym przejmować. Później w kursie omówimy szczegóły tego problemu i inne jego rozwiązania.
Dodatkowe informacje
Nieskończona pętla
Nieskończoną pętlę możemy utworzyć na dwa sposoby:
for(;;)
{
// ... kod
}
while(true)
{
// ... kod
}
Kod wykonywany w środku będzie się wykonywać w nieskończoność,
dopóki nie będzie przerwany wewnątrz (np. za pomocą instrukcji break, return (którą poznamy w lekcji o funkcjach),
czy wywołaniem funkcji, która nie powraca, np. std::exit)
Tego typu pętle są często wykorzystywane w programach w miejsach, gdzie ma działać kod obsługujący pewnego rodzaju zdarzenia. Np. nieskończona pętla obsługująca okienko graficzne, która obsługuje zdarzenia z systemu operacyjnego, pętla główna gry, etc.
Pętla w pętli
Kod pętli jest takim samym kodem, jaki możemy napisać praktycznie gdziekolwiek indziej, to znaczy,
że możemy również zapisać pętlę w pętli. Taki rodzaj pętli nazywamy pętlą zagnieżdżoną (tak samo instrukcję if w instrukcji if nazwiemy zagnieżdżoną instrukcją if).
Możemy to wykorzystać, żeby np. usunąć z listy zawodników drużyny e-sportowej każdego gracza, który przegrał:
#include <iostream>
#include <string>
#include <vector>
int main()
{
std::vector<std::string> teamMembers = { "Marek", "Karolina", "Arek", "Filip", "Maja" };
std::vector<std::string> membersThatLost = { "Maja", "Marek", "Arek" };
for(int i = 0; i < teamMembers.size(); i++)
{
for(int j = 0; j < membersThatLost.size(); j++)
{
if(teamMembers[i] == membersThatLost[j])
teamMembers.erase(teamMembers.begin() + i);
}
}
std::cout << "Gracze, którzy jeszcze żyją: ";
for(auto member : teamMembers)
{
std::cout << member << ' ';
}
}
Gracze, którzy jeszcze żyją: Karolina Filip
Konwencja i, j
Konwencją jest nazywanie zmiennej iteracyjnej w pętli i (skrót od iterator), oraz j w pętli zagnieżdżonej.
Jesli czujemy potrzebę nazwania naszej zmiennej inaczej, bardziej opisowo, to powinniśmy to robić,
jednak jeśli nasza pętla będzie zawierała stosunkowo mało kodu i będzie wiadomo czym jest i, to bez wahania można tę nazwę wykorzystać.


