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++.

Pętle C++ – podstawowe konstrukcje iteracyjne: for, while, do-while

W C++ istnieją trzy klasyczne konstrukcje pętli:

  • for
  • while
  • do { } 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:

  1. Sprawdzenie warunku logicznego.
  2. Jeśli warunek jest prawdziwy – wykonanie bloku.
  3. 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:

  1. Wykonanie inicjalizacji.
  2. Sprawdzenie warunku.
  3. Wykonanie bloku.
  4. Wykonanie modyfikacji.
  5. 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

  1. Brak modyfikacji zmiennej sterującej
    • prowadzi do pętli nieskończonej.
  2. 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.

  1. Modyfikacja kolekcji podczas iteracji
    • w przypadku wektorów może unieważnić iterator.
  2. Użycie nieprawidłowego typu licznika
    • np. unsigned przy porównaniach z wartością ujemną.
  3. 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++.