Programowanie

Goto

Goto

goto to nazwa często spotykanej w językach programowania instrukcji, która powoduje bezwarunkowe przekazanie sterowania w inne miejsce (tzw. skok). Występuje w tak odległych od siebie językach jak Fortran, Algol, COBOL, SNOBOL, Basic, C/C++, Perl, Pascal i wielu innych. Instrukcja goto może zaciemniać strukturę programu (zwłaszcza, gdy skok odbywa się między dwoma nie związanymi ze sobą blokami kodu, np. z ciała jednej klasy do drugiej), dlatego zaleca się unikać jej stosowania, zastępując goto innymi konstrukcjami programowymi.

Np. w poniższym kodzie w Perlu:

goto C;
A:
print "kota\n";
goto D;
B:
print "ma ";
goto A;
C:
print "Ala ";
goto B;
D:

Jak widać, czytelność bardzo ucierpiała w porównaniu z:

print "Ala ";
print "ma ";
print "kota\n";

Wynikiem działania obu powyższych programów jest wydrukowanie napisu "Ala ma kota", jednak drugi program jest o wiele prostszy do zrozumienia, napisania, zdebugowania i utrzymywania od pierwszego.

Nie zawsze jednak goto utrudnia odczytanie kodu. Nie należy na siłę unikać tej konstrukcji językowej. Jej zastosowanie może ułatwić zrozumienie programu.

Jako przykład weźmy sytuację, gdzie zachodzi potrzeba opuszczenia podwójnej pętli while (pętla zagnieżdżona w pętli). (Przypominam, że w C instrukcja break powoduje opuszczenie tylko jednej pętli)

  int i = 0;
  while(i<30)
  {
    while(i%10<9)
    {
      if(i==15)
        goto lab;
      printf("%d\n",++i);
    }
    ++i;
    ++i;
  }
  lab:
  printf("koniec\n\n");

Przedstawiony problem można rozwiać w inny sposób, np: ponownie sprawdzając warunek stopu (i tak przez 30 kolejnych pętli) lub tworząc funkcję dla tego fragmentu kodu i zastosowaniu instrukcji RETURN, lecz nie zawsze istnieje proste rozwiązanie...

[edytuj] Jak można sterować przepływem kontroli

Aby sterować przepływem kontroli w programie w sposób, który umożliwia zaprogramowanie każdego diagramu przepływu czy każdego automatu o skończonej liczbie stanów bez duplikacji kodu wystarczy jedna instrukcja if (warunek) { goto X; }, ze szczególnym przypadkiem if (prawda) { goto X; }. Tak jest to rozwiązane na poziomie asemblera - dostępne są jedynie instrukcje skoku bezwarunkowego (na Intelopochodnych JMP) i warunkowego (na Intelopochodnych JZ, JNZ, JG, JC itd, gdzie literki po J oznaczają stan flag oznaczających wynik ostatniej operacji).

W Fortranie pierwotnie jedyną instrukcją warunkową był właśnie skok - nie można było warunkowo przypisać wartości czy wykonać grupy poleceń. Było to z jednej strony całkowicie wystarczające, z drugiej zaś mało ekspresywne.

Z czasem dodawano nowe instrukcje kontroli przepływu: wykonania warunkowego (if (warunek) { POLECENIE1 } else { POLECENIE2 }, kilku typów pętli (while, for), funkcje rekurencyjne, instrukcje ponowienia (redo), następnej iteracji (next lub continue) lub zakończenia wykonywania (last lub break) pętli, te same instrukcje z wielopoziomowymi pętlami (X: foreach $a(@A) { foreach $b(@B) { ...; if (...) { last X; }} }), wyjątki, iteratory, funkcje wyższego rzędu, wątki itd.

Są one dużo bardziej ekspresywne od goto, którego programiści piszący w wysokopoziomowych językach programowania powinni unikać. Jednak czasami takiej możliwości nie mają piszący w językach z relatywnie ubogimi strukturami kontroli przepływu, takimi jak C czy Basic.

[edytuj] Zobacz też