Kurs C++: Pętle i Iteracje

Pętle while są potężnym narzędziem w programowaniu, ale łatwo popełnić błędy, które prowadzą do nieoczekiwanych zachowań. Oto najczęstsze pułapki oraz sposoby ich unikania:


1. Nieskończona pętla przez brak warunku zakończenia

Problem: Brak odpowiedniego warunku zakończenia prowadzi do pętli, która nigdy się nie kończy.

Przykład błędu:

#include <iostream>
using namespace std;

int main() {
    int licznik = 0;

    while (licznik < 5) {
        cout << "Licznik: " << licznik << endl;
        // Brak zwiększania zmiennej 'licznik' powoduje nieskończoną pętlę
    }

    return 0;
}

Rozwiązanie: Upewnij się, że w każdej iteracji zmienna sterująca jest odpowiednio modyfikowana.

Poprawna wersja:

#include <iostream>
using namespace std;

int main() {
    int licznik = 0;

    while (licznik < 5) {
        cout << "Licznik: " << licznik << endl;
        licznik++;  // Poprawne zwiększanie zmiennej sterującej
    }

    return 0;
}

2. Warunek, który nigdy nie jest spełniony

Problem: Jeśli warunek jest błędnie zapisany, pętla może nigdy się nie wykonać.

Przykład błędu:

#include <iostream>
using namespace std;

int main() {
    int licznik = 10;

    while (licznik < 5) {  // Ten warunek nigdy nie jest spełniony
        cout << "To się nigdy nie wykona!" << endl;
    }

    return 0;
}

Rozwiązanie: Upewnij się, że warunek logiczny jest poprawny i zmienna sterująca będzie miała szansę osiągnąć wymagany stan.

Poprawna wersja:

#include <iostream>
using namespace std;

int main() {
    int licznik = 10;

    while (licznik > 5) {  // Teraz warunek jest poprawny
        cout << "Licznik: " << licznik << endl;
        licznik--;
    }

    return 0;
}

3. Błąd związany z warunkiem logicznym

Problem: Użycie niewłaściwego operatora logicznego może prowadzić do nieoczekiwanych wyników.

Przykład błędu:

#include <iostream>
using namespace std;

int main() {
    int x = 10;

    while (x = 5) {  // BŁĄD: Użyto '=' zamiast '=='
        cout << "Wykonano pętlę" << endl;
        x--;  // Niebezpieczne: ta linia może nigdy nie zostać wykonana
    }

    return 0;
}

Rozwiązanie: Używaj operatora porównania ==, a nie przypisania = w warunkach pętli.

Poprawna wersja:

#include <iostream>
using namespace std;

int main() {
    int x = 10;

    while (x == 5) {  // Poprawny operator porównania
        cout << "Wykonano pętlę" << endl;
        x--;
    }

    return 0;
}

4. Niezmienna zmienna sterująca

Problem: Jeśli zmienna sterująca nie jest aktualizowana w pętli, pętla może nigdy nie zakończyć się.

Przykład błędu:

#include <iostream>
using namespace std;

int main() {
    int liczba = 1;

    while (liczba < 10) {
        cout << "Liczba: " << liczba << endl;
        // Brak aktualizacji zmiennej 'liczba'
    }

    return 0;
}

Rozwiązanie: Zadbaj, aby zmienna sterująca była zmieniana w każdej iteracji.

Poprawna wersja:

#include <iostream>
using namespace std;

int main() {
    int liczba = 1;

    while (liczba < 10) {
        cout << "Liczba: " << liczba << endl;
        liczba++;  // Dodano inkrementację
    }

    return 0;
}

5. Brak zabezpieczenia przed błędnym wejściem użytkownika

Problem: Jeśli użytkownik poda niepoprawne dane, pętla może utknąć w nieskończonym cyklu.

Przykład błędu:

#include <iostream>
using namespace std;

int main() {
    int liczba;

    cout << "Podaj liczbę większą od 0: ";
    cin >> liczba;

    while (liczba <= 0) {
        cout << "Błąd! Podaj liczbę większą od 0: ";
        cin >> liczba;
    }

    cout << "Prawidłowa liczba: " << liczba << endl;

    return 0;
}

Potencjalny problem: Jeśli użytkownik poda znak (np. a), program utknie w nieskończonej pętli.

Rozwiązanie: Dodaj zabezpieczenie sprawdzające poprawność wejścia.

Poprawna wersja z walidacją danych:

#include <iostream>
using namespace std;

int main() {
    int liczba;

    while (true) {
        cout << "Podaj liczbę większą od 0: ";
        cin >> liczba;

        if (cin.fail()) {
            cin.clear();  // Czyści błąd strumienia
            cin.ignore(1000, '\n');  // Usuwa nieprawidłowe dane z bufora
            cout << "Niepoprawne dane! Spróbuj ponownie." << endl;
        } else if (liczba > 0) {
            break;  // Poprawna wartość kończy pętlę
        }
    }

    cout << "Prawidłowa liczba: " << liczba << endl;

    return 0;
}

6. Zbyt skomplikowane warunki

Problem: Warunki zawierające zbyt wiele operatorów logicznych mogą być trudne do zrozumienia i prowadzić do błędów.

Przykład błędu:

while ((x < 10 && y > 5) || (z == 0 && k != 3) && a > 1) { ... }

Rozwiązanie: Rozbij złożone warunki na prostsze kroki.

Poprawna wersja z czytelniejszymi warunkami:

while ((x < 10 && y > 5) || (z == 0 && k != 3)) {
    if (a <= 1) {
        break;
    }
}

7. Brak limitu iteracji przy testowaniu

Problem: Podczas testowania kodu pętla może przypadkowo działać w nieskończoność.

Rozwiązanie: Dodaj mechanizm awaryjnego przerwania po określonej liczbie iteracji.

Przykład z limitem iteracji:

#include <iostream>
using namespace std;

int main() {
    int licznik = 0;

    while (true) {
        cout << "Iteracja #" << licznik << endl;
        licznik++;

        if (licznik == 100) {  // Awaryjne zakończenie po 100 iteracjach
            cout << "Limit iteracji osiągnięty." << endl;
            break;
        }
    }

    return 0;
}

Podsumowanie

✅ Zadbaj o poprawne warunki zakończenia.
✅ Upewnij się, że zmienne sterujące są odpowiednio aktualizowane.
✅ Stosuj mechanizmy awaryjnego przerwania (break) w krytycznych miejscach.
✅ Weryfikuj dane wejściowe od użytkownika, aby uniknąć niespodziewanych pętli.