Instrukcja goto w C++ pozwala na skok do innego miejsca w kodzie, oznaczonego etykietą. Może wydawać się użyteczna, ale jej stosowanie jest odradzane, ponieważ utrudnia czytelność kodu i prowadzi do tzw. „spaghetti code”.
1. Składnia instrukcji goto
goto etykieta; // Skok do etykiety
// ... (kod, który zostanie pominięty)
etykieta:
// Kod, który zostanie wykonany po skoku
🔹 Przykład użycia goto w pętli:
#include <iostream>
using namespace std;
int main() {
int liczba;
poczatek: // Etykieta
cout << "Podaj liczbę większą niż 10: ";
cin >> liczba;
if (liczba <= 10) {
cout << "Liczba za mała! Spróbuj ponownie." << endl;
goto poczatek; // Skok do początku
}
cout << "Dziękuję! Podano poprawną liczbę." << endl;
return 0;
}
✅ Działa poprawnie, ale kod jest trudniejszy do śledzenia niż w przypadku użycia pętli.
2. Dlaczego należy unikać goto?
2.1. Tworzy „spaghetti code”
Kod staje się chaotyczny, jeśli goto jest używane wiele razy, ponieważ trudno śledzić, skąd i dokąd następują skoki.
🔴 Przykład złego użycia goto:
#include <iostream>
using namespace std;
int main() {
int x = 1;
start:
cout << x << " ";
x++;
if (x <= 5) goto start; // Skok do etykiety
return 0;
}
❌ Problem: Kod jest nieczytelny, zamiast goto lepiej użyć pętli while:
int x = 1;
while (x <= 5) {
cout << x << " ";
x++;
}
✅ Efekt ten sam, ale kod czytelniejszy!
2.2. Utrudnia debugowanie
Jeśli program jest skomplikowany, a goto skacze do wielu miejsc, trudno znaleźć błąd i zrozumieć, jak kod działa.
🔴 Problem:
gotoomija naturalny przepływ sterowania.- Debugowanie wymaga skakania między etykietami, co utrudnia analizę kodu.
2.3. Powoduje wycieki pamięci
Jeśli goto omija zwalnianie pamięci lub zamykanie plików, może prowadzić do błędów.
🔴 Zły przykład (pomija delete):
int* ptr = new int(5);
goto koniec; // Pomija zwolnienie pamięci
delete ptr;
koniec:
cout << "Program zakończony.";
❌ Pamięć nie zostaje zwolniona, co powoduje wyciek!
✅ Lepiej użyć if lub return, by nie pominąć delete.
3. Kiedy można używać goto?
Mimo wad goto może być użyteczne w nielicznych przypadkach, np. do awaryjnego wyjścia z wielu zagnieżdżonych pętli.
✔ Przykład – wyjście z wielu pętli naraz:
#include <iostream>
using namespace std;
int main() {
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
cout << "i = " << i << ", j = " << j << endl;
if (i == 2 && j == 2) {
goto koniec; // Przerywa obie pętle
}
}
}
koniec:
cout << "Pętla przerwana!" << endl;
return 0;
}
✅ Działa, ale lepiej użyć break z flagą kontrolną.
4. Jak unikać goto?
✅ Zamień goto na pętle (for, while) lub instrukcje warunkowe (if-else).
✅ Zamiast goto w obsłudze błędów używaj wyjątków (try-catch).
✅ Aby wyjść z wielu pętli, użyj flagi (bool przerwij = true).
✔ Przykład – zamiast goto, użycie flagi:
#include <iostream>
using namespace std;
int main() {
bool przerwij = false;
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
cout << "i = " << i << ", j = " << j << endl;
if (i == 2 && j == 2) {
przerwij = true;
break; // Przerywa wewnętrzną pętlę
}
}
if (przerwij) break; // Przerywa także zewnętrzną pętlę
}
cout << "Pętla przerwana!" << endl;
return 0;
}
✅ Efekt ten sam, ale kod bardziej czytelny!
5. Podsumowanie – dlaczego unikać goto?
❌ goto prowadzi do „spaghetti code” – kod jest trudny do zrozumienia.
❌ Debugowanie skoków w kodzie jest męczące.
❌ Może prowadzić do błędów i wycieków pamięci.
✅ Zamiast goto, lepiej używać pętli, if-else, wyjątków lub flag sterujących.
✅ Jedyny sensowny przypadek użycia goto to awaryjne wyjście z kilku zagnieżdżonych pętli – ale lepiej użyć flagi.