Pętle
Pętle są jednym z podstawowych mechanizmów sterowania przepływem programu. Umożliwiają wielokrotne wykonywanie tego samego fragmentu kodu przy spełnieniu określonego warunku lub dla ustalonego zakresu wartości. Bez nich praktycznie nie da się pisać programów operujących na tablicach, danych wejściowych, plikach czy strukturach dynamicznych. W praktyce większość realnych algorytmów opiera się na iteracji. W języku C++ mechanizmy iteracyjne są rozbudowane i obejmują kilka konstrukcji składniowych, które różnią się semantyką i zastosowaniem. Właśnie temu zagadnieniu poświęcone są Pętle C++.
Spis treści
Pętle C++ – podstawowe konstrukcje iteracyjne: for, while, do-while
W C++ istnieją trzy klasyczne konstrukcje pętli:
forwhiledo { } while
Każda z nich realizuje ten sam ogólny cel: powtarzanie bloku instrukcji. Różnią się jednak miejscem sprawdzania warunku oraz typowym zastosowaniem.
Pętla while
Składnia:
while (warunek) {
instrukcje;
}Mechanizm działania:
- Sprawdzenie warunku logicznego.
- Jeśli warunek jest prawdziwy – wykonanie bloku.
- Powrót do punktu 1.
Jeżeli warunek od początku jest fałszywy, blok nie zostanie wykonany ani razu.
Przykład – wypisanie liczb od 0 do 4:
#include <iostream>int main() {
int i = 0; while (i < 5) {
std::cout << i << std::endl;
i++;
} return 0;
}Istotne jest, że modyfikacja zmiennej sterującej musi nastąpić wewnątrz bloku. Jeżeli jej zabraknie, powstanie pętla nieskończona.
Odpowiednik w C:
#include <stdio.h>int main() {
int i = 0; while (i < 5) {
printf("%d\n", i);
i++;
} return 0;
}Odpowiednik w Pythonie:
i = 0while i < 5:
print(i)
i += 1
Pętla do-while
Składnia:
do {
instrukcje;
} while (warunek);Różnica względem while polega na tym, że warunek sprawdzany jest na końcu. Oznacza to, że blok wykona się co najmniej jeden raz.
Przykład – wczytywanie liczby dodatniej:
#include <iostream>int main() {
int x; do {
std::cout << "Podaj liczbe dodatnia: ";
std::cin >> x;
} while (x <= 0); return 0;
}Ten mechanizm stosuje się wtedy, gdy pierwsze wykonanie musi nastąpić niezależnie od warunku, np. przy walidacji danych wejściowych.
W Pythonie brak dokładnego odpowiednika, symuluje się to przez:
while True:
x = int(input("Podaj liczbe dodatnia: "))
if x > 0:
break
Pętla for
Najbardziej klasyczna konstrukcja iteracyjna.
Składnia:
for (inicjalizacja; warunek; modyfikacja) {
instrukcje;
}Rozkład logiczny:
- Wykonanie inicjalizacji.
- Sprawdzenie warunku.
- Wykonanie bloku.
- Wykonanie modyfikacji.
- Powrót do punktu 2.
Przykład:
#include <iostream>int main() {
for (int i = 0; i < 5; i++) {
std::cout << i << std::endl;
}
return 0;
}Zmienna i ma zasięg ograniczony do pętli. To ważne z punktu widzenia bezpieczeństwa kodu.
Odpowiednik w C:
#include <stdio.h>int main() {
for (int i = 0; i < 5; i++) {
printf("%d\n", i);
}
return 0;
}W Pythonie:
for i in range(5):
print(i)
Pętle C++ – zasięg zmiennych, czas życia i wpływ na poprawność algorytmu
W C++ zmienna zadeklarowana w nagłówku pętli for:
for (int i = 0; i < 10; i++)
istnieje tylko wewnątrz tej pętli. Po jej zakończeniu nie można się do niej odwołać.
Natomiast w while:
int i = 0;
while (i < 10) {
i++;
}
i istnieje również po zakończeniu pętli.
To ma znaczenie przy bardziej złożonych algorytmach, np. przy przeszukiwaniu tablicy:
int i;
for (i = 0; i < n; i++) {
if (tab[i] == x)
break;
}if (i < n) {
std::cout << "Znaleziono";
}
Jeżeli i byłoby zadeklarowane w nagłówku, nie byłoby dostępne po zakończeniu pętli.
Czas życia zmiennej wpływa też na optymalizację – kompilator może łatwiej zarządzać pamięcią, gdy zakres jest ograniczony.
Pętle C++ – sterowanie przebiegiem: break, continue, return, goto
break
Natychmiast przerywa działanie pętli.
for (int i = 0; i < 10; i++) {
if (i == 5)
break;
std::cout << i << std::endl;
}Wypisze 0-4.
continue
Pomija resztę bieżącej iteracji.
for (int i = 0; i < 5; i++) {
if (i == 2)
continue;
std::cout << i << std::endl;
}Wypisze 0,1,3,4.
return
Kończy funkcję – a więc i pętlę.
while (true) {
int x;
std::cin >> x;
if (x == 0)
return 0;
}goto
Możliwy, ale w praktyce unika się go. Może prowadzić do kodu trudnego w analizie.
Pętle C++ – pętle zagnieżdżone i złożoność obliczeniowa
Pętla może zawierać inną pętlę.
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
std::cout << i << " " << j << std::endl;
}
}Liczba wykonań wynosi 3 × 3 = 9.
Z punktu widzenia analizy algorytmów:
- jedna pętla do
n→ O(n) - dwie zagnieżdżone → O(n²)
- trzy → O(n³)
Przykład sumowania macierzy:
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
suma += tab[i][j];
}
}Złożoność: O(nm).
W Pythonie analogicznie:
for i in range(n):
for j in range(m):
suma += tab[i][j]
Pętle C++ – pętla zakresowa (range-based for) i iteracja po kontenerach
Od C++11 dostępna jest pętla zakresowa:
for (typ zmienna : kolekcja) {
instrukcje;
}Przykład z tablicą:
int tab[5] = {1,2,3,4,5};for (int x : tab) {
std::cout << x << std::endl;
}Dla kontenerów STL:
#include <vector>
#include <iostream>int main() {
std::vector<int> v = {1,2,3}; for (int x : v) {
std::cout << x << std::endl;
} return 0;
}
Jeżeli chcemy modyfikować elementy, używamy referencji:
for (int &x : v) {
x *= 2;
}Brak referencji powoduje pracę na kopii.
Pętle C++ – pętle nieskończone i ich kontrolowane zastosowanie
Pętla nieskończona:
while (true) {
// ...
}lub
for (;;) {
// ...
}Stosowana w:
- serwerach
- programach interaktywnych
- systemach wbudowanych
Warunek zakończenia musi być realizowany przez break, return lub sygnał zewnętrzny.
Pętle C++ – typowe błędy logiczne i problemy implementacyjne
- Brak modyfikacji zmiennej sterującej
- prowadzi do pętli nieskończonej.
- Błąd granicy (off-by-one)
for (int i = 0; i <= n; i++)
Jeżeli tablica ma rozmiar n, ostatni poprawny indeks to n-1.
- Modyfikacja kolekcji podczas iteracji
- w przypadku wektorów może unieważnić iterator.
- Użycie nieprawidłowego typu licznika
- np.
unsignedprzy porównaniach z wartością ujemną.
- np.
- Zbyt złożone warunki w nagłówku
- utrudniają analizę poprawności.
Pętle C++ – zależność między konstrukcją pętli a czytelnością algorytmu
Dobór rodzaju pętli powinien wynikać z charakteru problemu:
- znana liczba iteracji →
for - iteracja zależna od warunku logicznego →
while - konieczność wykonania co najmniej jednej iteracji →
do-while - przejście po kolekcji → range-based for
Nie chodzi tylko o składnię, ale o semantykę. Dobrze dobrana konstrukcja upraszcza analizę poprawności i zmniejsza ryzyko błędów.
W algorytmach klasycznych (sortowanie, przeszukiwanie, operacje na grafach) konstrukcja pętli bezpośrednio przekłada się na złożoność czasową i pamięciową. W systemach czasu rzeczywistego dodatkowo znaczenie ma deterministyczność liczby iteracji.
Na poziomie podstawowym pętle wydają się prostą konstrukcją składniową, ale w praktyce są fundamentem całej struktury algorytmicznej programu. Różnice między for, while i do-while, kwestie zasięgu zmiennych, kontrola przepływu oraz wpływ na złożoność obliczeniową mają realne konsekwencje w projektowaniu oprogramowania. Zrozumienie tych mechanizmów na poziomie semantycznym, a nie tylko składniowym, jest konieczne do pisania poprawnych i przewidywalnych programów w C++.