Czy korzystnie jest używać 'goto' w języku, który obsługuje pętle i funkcje? Jeśli tak, to dlaczego?

Od dawna mam wrażenie, że goto nigdy nie powinny być używane, jeśli to możliwe. Podczas przeglądania libavcodec (który jest napisany w C) na drugi dzień, zauważyłem wiele zastosowań tego. Czy korzystnie jest używać goto w języku obsługującym pętle i funkcje? Jeśli tak, to dlaczego?

Author: chqrlie, 2008-08-23

26 answers

Istnieje kilka powodów, dla których używam stwierdzenia "goto", o których wiem (niektórzy już z tym rozmawiali):

Cleanly exiting a function

Często w funkcji można przydzielić zasoby i trzeba wyjść w wielu miejscach. Programiści mogą uprościć swój kod, umieszczając kod czyszczenia zasobów na końcu funkcji, a wszystkie "punkty wyjścia" funkcji otrzymają Etykietę czyszczenia. W ten sposób nie musisz pisać kodu czyszczenia przy każdym " wyjściu punkt " funkcji.

Wyjście zagnieżdżonych pętli

Jeśli znajdujesz się w zagnieżdżonej pętli i musisz wyłamać się z wszystkich pętli, goto może uczynić to o wiele czystszym i prostszym niż instrukcje break I if-checks.

Niskopoziomowe ulepszenia wydajności

Jest to ważne tylko w kodzie perf-critical, ale polecenia goto wykonują się bardzo szybko i mogą dać ci impuls podczas przechodzenia przez funkcję. Jest to miecz obosieczny, ponieważ kompilator zazwyczaj nie może zoptymalizować kodu zawierającego gotos.

Zauważ, że we wszystkich tych przykładach Goto są ograniczone do zakresu jednej funkcji.

 254
Author: Chris Gillum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-09 13:05:22

Każdy, kto jest anty - goto cytuje, bezpośrednio lub pośrednio, artykuł Edsgera DijkstryGoTo uznany za szkodliwy , aby uzasadnić swoje stanowisko. Szkoda, że artykuł Dijkstry nie ma praktycznie nic wspólnego ze sposobem, w jaki w dzisiejszych czasach używane są wyrażenia goto, a więc to, co mówi artykuł, ma niewiele wspólnego ze współczesną sceną programowania. goto-less meme graniczy teraz z religią, aż do jej pism dyktowanych z wysokości, jej arcykapłanów i unikanie (lub co gorsza) postrzeganych heretyków.

Przedstawmy artykuł Dijkstry w kontekście, aby rzucić trochę światła na ten temat.

Kiedy Dijkstra napisał swoją pracę, popularne języki w tamtych czasach były niestrukturalnymi językami proceduralnymi, takimi jak podstawowe, FORTRAN (wcześniejsze dialekty) i różne języki asemblacji. Dość często ludzie używający języków wyższego poziomu przeskakiwali po całej bazie kodu w skręconych, wypaczonych wątkach wykonania, które dały początek określenie "Kodeks spaghetti". Możesz to zobaczyć, przechodząc do klasycznej gry Trek napisanej przez Mike ' a Mayfielda i próbując dowiedzieć się, jak to działa. Poświęć chwilę, żeby to przejrzeć.

Dijkstra w swojej pracy z 1968 r.sprzeciwiał się temu "nieokiełznanemu użyciu Oświadczenia go to". To środowisko, w którym żył, skłoniło go do napisania tej pracy. Możliwość skakania w dowolnym miejscu w kodzie w dowolnym momencie co krytykował i domagał się zaprzestania. Porównywanie tego do anemicznych potęg goto W C lub innych tego typu bardziej współczesnych językach jest po prostu ryzykowne.

[22]}już słyszę podniesione śpiewy kultystów, gdy stawiają czoło heretykom. "Ale, "będą śpiewać," możesz sprawić, że kod będzie bardzo trudny do odczytania z goto W C. " O tak? Możesz sprawić, że kod będzie bardzo trudny do odczytania bez goto. Jak ten:
#define _ -F<00||--F-OO--;
int F=00,OO=00;main(){F_OO();printf("%1.3f\n",4.*-F/OO/OO);}F_OO()
{
            _-_-_-_
       _-_-_-_-_-_-_-_-_
    _-_-_-_-_-_-_-_-_-_-_-_
  _-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
  _-_-_-_-_-_-_-_-_-_-_-_-_-_
    _-_-_-_-_-_-_-_-_-_-_-_
        _-_-_-_-_-_-_-_
            _-_-_-_
}

NIE goto w zasięgu wzroku, więc musi być łatwy do odczytania, prawda? A może ten:

a[900];     b;c;d=1     ;e=1;f;     g;h;O;      main(k,
l)char*     *l;{g=      atoi(*      ++l);       for(k=
0;k*k<      g;b=k       ++>>1)      ;for(h=     0;h*h<=
g;++h);     --h;c=(     (h+=g>h     *(h+1))     -1)>>1;
while(d     <=g){       ++O;for     (f=0;f<     O&&d<=g
;++f)a[     b<<5|c]     =d++,b+=    e;for(      f=0;f<O
&&d<=g;     ++f)a[b     <<5|c]=     d++,c+=     e;e= -e
;}for(c     =0;c<h;     ++c){       for(b=0     ;b<k;++
b){if(b     <k/2)a[     b<<5|c]     ^=a[(k      -(b+1))
<<5|c]^=    a[b<<5      |c]^=a[     (k-(b+1     ))<<5|c]
;printf(    a[b<<5|c    ]?"%-4d"    :"    "     ,a[b<<5
|c]);}      putchar(    '\n');}}    /*Mike      Laman*/

NIE goto tam też. Musi być zatem czytelny.

O co mi chodzi z tymi przykładami? To nie funkcje językowe czynią nieczytelny, niemożliwy do utrzymania kod. To nie składnia to robi. To źli Programiści to powodują. A źli Programiści, jak widać w powyższym punkcie, mogą sprawić, że Dowolna funkcja języka będzie nieczytelna i bezużyteczna. Jak pętle for. (Możesz je zobaczyć, prawda?)

Teraz aby być uczciwym, niektóre konstrukcje językowe są łatwiejsze do nadużywania niż inne. Jeśli jednak jesteś programistą C, przyjrzałbym się bliżej około 50% zastosowań #define na długo przed wyruszeniem na krucjatę przeciwko goto!

Więc, dla tych, którzy zadali sobie trud, aby przeczytać tak daleko, jest kilka kluczowych punktów do odnotowania.

  1. praca Dijkstry o goto wypowiedziach została napisana dla środowiska programistycznego, w którym goto było lotem bardziej potencjalnie szkodliwe niż w większości współczesnych języki, które nie są asemblerem.
  2. automatyczne odrzucenie wszystkich zastosowań goto z tego powodu jest tak racjonalne, jak powiedzenie " próbowałem dobrze się bawiłem, ale nie podobało mi się to, więc teraz jestem przeciw".
  3. istnieją uzasadnione zastosowania nowoczesnych (anemicznych) goto stwierdzeń w kodzie, które nie mogą być odpowiednio zastąpiony przez inne konstrukcje.
  4. istnieją, oczywiście, bezprawne zastosowania tych samych stwierdzeń.
  5. są też bezprawne zastosowania nowoczesnej kontroli wyrażenia takie jak" godo", gdzie pętla zawsze-false do jest łamana z użycia break zamiast goto. Są one często gorsze niż rozsądne użycie goto.
 917
Author: JUST MY correct OPINION,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-03-05 21:11:08

Ślepe przestrzeganie najlepszych praktyk nie jest najlepszą praktyką. Ideą unikania instrukcji goto jako podstawowej formy kontroli przepływu jest unikanie tworzenia nieczytelnego kodu spaghetti. Jeśli używane są oszczędnie we właściwych miejscach, mogą być czasami najprostszym, najczystszym sposobem wyrażenia idei. Walter Bright, twórca kompilatora C++ Zortech i języka programowania D, używa ich często, ale rozsądnie. Nawet ze stwierdzeniami goto, jego kod jest nadal doskonale czytelny.

Podsumowując: unikanie goto dla uniknięcia goto jest bezcelowe. To, czego naprawdę chcesz uniknąć, to tworzenie nieczytelnego kodu. Jeśli twój goto - załadowany kod jest czytelny, to nie ma w nim nic złego.

 160
Author: dsimcha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-02 20:05:53

Ponieważ goto utrudnia rozumowanie o przepływie programu1 (aka. "spaghetti code"), goto jest zwykle używane tylko w celu zrekompensowania brakujących funkcji: użycie goto może być rzeczywiście dopuszczalne, ale tylko wtedy, gdy język nie oferuje bardziej ustrukturyzowanego wariantu, aby uzyskać ten sam cel. Weźmy przykład wątpliwości:

Zasada goto, której używamy jest taka, że goto jest w porządku, aby przeskakiwać do przodu do pojedynczego punktu czyszczenia wyjścia w funkcji.

To prawda-ale tylko wtedy, gdy język nie pozwala na strukturyzowaną obsługę wyjątków za pomocą kodu czyszczenia (takiego jak RAII lub finally), który wykonuje tę samą pracę lepiej (ponieważ jest specjalnie do tego stworzony), lub gdy istnieje dobry powód, aby nie stosować strukturalnej obsługi wyjątków (ale nigdy nie będziesz mieć tego przypadku, z wyjątkiem bardzo niskiego poziomu).

W większości innych języków, jedynym dopuszczalnym użyciem goto jest wyjście z zagnieżdżonych pętli. I nawet tam prawie zawsze lepiej jest podnieść zewnętrzną pętlę do własnej metody i zamiast tego użyj return.

Poza tym, goto jest znakiem, że nie ma wystarczającej ilości myśli w danym kawałku kodu.


1 współczesne języki, które obsługują goto implementują pewne ograniczenia (np. goto mogą nie wchodzić lub wychodzić z funkcji), ale problem pozostaje zasadniczo taki sam.

Nawiasem mówiąc, to samo dotyczy również innych cech językowych, w szczególności WYJĄTKÓW. I zwykle obowiązują ścisłe zasady, aby tylko używaj tych funkcji tam, gdzie jest to wskazane, takich jak reguła, aby nie używać WYJĄTKÓW do kontrolowania Nie wyjątkowego przepływu programu.

 38
Author: Konrad Rudolph,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-17 14:31:02

Cóż, jest jedna rzecz, która jest zawsze gorsza od goto's; dziwne użycie innych operatorów programflow, aby uniknąć goto:

Przykłady:

    // 1
    try{
      ...
      throw NoErrorException;
      ...
    } catch (const NoErrorException& noe){
      // This is the worst
    } 


    // 2
    do {
      ...break; 
      ...break;
    } while (false);


    // 3
    for(int i = 0;...) { 
      bool restartOuter = false;
      for (int j = 0;...) {
        if (...)
          restartOuter = true;
      if (restartOuter) {
        i = -1;
      }
    }

etc
etc
 36
Author: Viktor Sehr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-11 09:06:33

W C# Switch statement doest not allow fall-through. Tak więc gotojest używane do przeniesienia sterowania do określonej etykiety switch-case lub domyślnej etykiety .

Na przykład:

switch(value)
{
  case 0:
    Console.Writeln("In case 0");
    goto case 1;
  case 1:
    Console.Writeln("In case 1");
    goto case 2;
  case 2:
    Console.Writeln("In case 2");
    goto default;
  default:
    Console.Writeln("In default");
    break;
}

Edit: jest jeden wyjątek na zasadzie "no fall-through". Fall-through jest dozwolone, jeśli instrukcja case nie ma kodu.

 28
Author: Jakub Šturc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-17 18:36:10

#ifdef TONGUE_IN_CHEEK

Perl ma goto, który pozwala na implementację wywołań ogonowych biedaka. :- P

sub factorial {
    my ($n, $acc) = (@_, 1);
    return $acc if $n < 1;
    @_ = ($n - 1, $acc * $n);
    goto &factorial;
}

#endif

Ok, więc to nie ma nic wspólnego z C goto. Bardziej poważnie, zgadzam się z innymi uwagami na temat używania goto do sprzątania, lub implementacji urządzenia Duffa, lub tym podobnych. Chodzi o używanie, a nie nadużywanie.

(ten sam komentarz może dotyczyć longjmp, wyjątki, call/cc, i podobne - - - mają legalne zastosowania, ale mogą być łatwo maltretowany. Na przykład, rzucanie wyjątku wyłącznie po to, aby uciec głęboko zagnieżdżonej strukturze kontroli, w zupełnie nie wyjątkowych okolicznościach.)

 14
Author: Chris Jester-Young,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-25 02:53:37

Przez lata napisałem więcej niż kilka linii języka asemblera. Ostatecznie każdy język wysokiego poziomu kompiluje się do gotos. Ok, nazwijmy je "gałęziami" albo "skokami" czy czymkolwiek innym, ale to gotos. Czy ktoś może napisać Goto-less assembler?

Teraz pewnie, możesz wskazać programiście Fortran, C lub BASIC, że uruchamianie z gotos to przepis na spaghetti bolognaise. Odpowiedzią nie jest jednak unikanie ich, ale ostrożne korzystanie z nich.

Nóż może być użyty do przygotuj jedzenie, uwolnij kogoś lub zabij kogoś. Czy obchodzimy się bez noży ze strachu przed tymi ostatnimi? Podobnie goto: używane niedbale przeszkadza, używane ostrożnie pomaga.

 14
Author: bugmagnet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 17:25:28

Spójrz na Kiedy używać Goto podczas programowania w C :

Chociaż używanie goto jest prawie zawsze złą praktyką programistyczną (z pewnością można znaleźć lepszy sposób na robienie XYZ), są chwile, kiedy naprawdę nie jest to zły wybór. Niektórzy mogą nawet twierdzić, że gdy jest to przydatne, jest to najlepszy wybór.

Większość tego, co mam do powiedzenia na temat goto dotyczy tylko C. Jeśli używasz C++, nie ma żadnego powodu, aby używać goto zamiast WYJĄTKÓW. W C, jednak Ty nie masz mocy mechanizmu obsługi wyjątków, więc jeśli chcesz oddzielić obsługę błędów od reszty logiki programu i chcesz uniknąć wielokrotnego przepisywania kodu oczyszczającego w całym kodzie, goto może być dobrym wyborem.

Co mam na myśli? Możesz mieć jakiś kod, który wygląda tak:
int big_function()
{
    /* do some work */
    if([error])
    {
        /* clean up*/
        return [error];
    }
    /* do some more work */
    if([error])
    {
        /* clean up*/
        return [error];
    }
    /* do some more work */
    if([error])
    {
        /* clean up*/
        return [error];
    }
    /* do some more work */
    if([error])
    {
        /* clean up*/
        return [error];
    }
    /* clean up*/
    return [success];
}

To jest w porządku, dopóki nie zdasz sobie sprawy, że musisz zmienić swój kod czyszczenia. Następnie musisz przejść i wprowadzić 4 zmiany. Teraz możesz zdecydować że można po prostu zamknąć wszystkie sprzątanie w jednej funkcji; to nie jest zły pomysł. Ale to oznacza, że musisz być ostrożny ze wskaźnikami - jeśli planujesz uwolnić wskaźnik w funkcji czyszczenia, nie ma sposobu, aby ustawić go na wartość null, chyba że podasz wskaźnik do wskaźnika. W wielu przypadkach i tak nie będziesz używać tego wskaźnika, więc może to nie być poważny problem. Z drugiej strony, jeśli dodasz nowy wskaźnik, uchwyt pliku lub inną rzecz, która wymaga cleanup, następnie musisz ponownie zmienić funkcję cleanup; a następnie musisz zmienić argumenty na tę funkcję.

Używając goto, będzie to

int big_function()
{
    int ret_val = [success];
    /* do some work */
    if([error])
    {
        ret_val = [error];
        goto end;
    }
    /* do some more work */
    if([error])
    {
        ret_val = [error];
        goto end;
    }
    /* do some more work */
    if([error])
    {
        ret_val = [error];
        goto end;
    }
    /* do some more work */
    if([error])
    {
        ret_val = [error];
        goto end;
    }
end:
    /* clean up*/
    return ret_val;
}

Zaletą jest to, że Twój kod po zakończeniu ma dostęp do wszystkiego, czego będzie potrzebował, aby wykonać czyszczenie, i udało Ci się znacznie zmniejszyć liczbę punktów zmian. Kolejną korzyścią jest to, że z wielu punktów wyjścia dla swojej funkcji zamieniłeś się w tylko jeden; nie ma szans, że przypadkowo powrót z funkcji bez czyszczenia.

Ponadto, ponieważ goto jest używane tylko do przeskakiwania do jednego punktu, to nie jest tak, że tworzysz masę kodu spaghetti przeskakując tam iz powrotem, próbując symulować wywołania funkcji. Raczej goto pomaga w pisaniu bardziej ustrukturyzowanego kodu.


Jednym słowem, {[2] } powinno być zawsze używane oszczędnie i jako ostateczność-ale jest na to czas i miejsce. Pytanie nie powinno brzmieć "Czy musisz z niego korzystać", ale "czy jest to najlepszy wybór", aby go użyć.

 8
Author: herohuyongtao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55

Jednym z powodów, dla których goto jest złe, oprócz stylu kodowania jest to, że można go użyć do tworzenia nakładających się na siebie, ale nie zagnieżdżonych pętli:

loop1:
  a
loop2:
  b
  if(cond1) goto loop1
  c
  if(cond2) goto loop2

To stworzyłoby dziwaczną, ale być może legalną strukturę przepływu kontroli, gdzie Sekwencja taka jak (A, b, c, b, A, B, B,...) jest możliwe, co sprawia, że hakerzy kompilatorów są nieszczęśliwi. Najwyraźniej istnieje wiele sprytnych sztuczek optymalizacyjnych, które polegają na tym, że nie występuje tego typu struktura. (Powinienem sprawdzić kopię Smoka książka...) Wynikiem tego może (przy użyciu niektórych kompilatorów) być to, że inne optymalizacje nie są wykonywane dla kodu zawierającego goto s.

To może być przydatne, jeśli wiesz to po prostu, "oh, przy okazji", zdarza się przekonać kompilator do emitowania szybszego kodu. Osobiście wolałbym spróbować wyjaśnić kompilatorowi, co jest prawdopodobne, a co nie, zanim użyję sztuczki takiej jak goto, ale prawdopodobnie mogę również spróbować goto przed hakowaniem assemblera.

 7
Author: Anders Eurenius,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-23 19:50:37

Uważam za zabawne, że niektórzy posuną się tak daleko, aby podać listę przypadków, w których goto jest dopuszczalne, mówiąc, że wszystkie inne zastosowania są niedopuszczalne. Naprawdę myślisz, że znasz każdy przypadek, w którym goto jest najlepszym wyborem do wyrażenia algorytmu?

Dla zilustrowania, podam przykład, którego jeszcze nikt tutaj nie pokazał:

Dzisiaj pisałem kod do wstawiania elementu do tabeli hash. Tabela hash jest buforem poprzednich obliczeń, które można nadpisać w Wola (wpływająca na wydajność, ale nie poprawność).

Każde wiadro tabeli hash ma 4 Sloty i mam kilka kryteriów, aby zdecydować, który element zastąpić, gdy wiadro jest pełne. W tej chwili oznacza to wykonanie do trzech przejść przez wiadro, jak to:

// Overwrite an element with same hash key if it exists
for (add_index=0; add_index < ELEMENTS_PER_BUCKET; add_index++)
  if (slot_p[add_index].hash_key == hash_key)
    goto add;

// Otherwise, find first empty element
for (add_index=0; add_index < ELEMENTS_PER_BUCKET; add_index++)
  if ((slot_p[add_index].type == TT_ELEMENT_EMPTY)
    goto add;

// Additional passes go here...

add:
// element is written to the hash table here
Gdybym nie używał goto, jak wyglądałby ten kod?

Coś takiego:

// Overwrite an element with same hash key if it exists
for (add_index=0; add_index < ELEMENTS_PER_BUCKET; add_index++)
  if (slot_p[add_index].hash_key == hash_key)
    break;

if (add_index >= ELEMENTS_PER_BUCKET) {
  // Otherwise, find first empty element
  for (add_index=0; add_index < ELEMENTS_PER_BUCKET; add_index++)
    if ((slot_p[add_index].type == TT_ELEMENT_EMPTY)
      break;
  if (add_index >= ELEMENTS_PER_BUCKET)
   // Additional passes go here (nested further)...
}

// element is written to the hash table here

Wyglądałoby coraz gorzej, gdyby Dodano więcej przejść, podczas gdy wersja z goto zachowuje to samo poziom wcięcia przez cały czas i unika użycia fałszywych instrukcji if, których wynik jest implikowany przez wykonanie poprzedniej pętli.

Więc jest jeszcze jeden przypadek, w którym goto sprawia, że kod jest czystszy i łatwiejszy do napisania i zrozumienia... Jestem pewien, że jest ich wiele więcej, więc nie udawaj, że znasz wszystkie przypadki, w których goto jest przydatne, dissując te dobre, o których nie mogłeś pomyśleć.
 7
Author: Ricardo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-23 08:33:38

Zasada goto, której używamy, jest taka, że goto jest w porządku, aby przeskakiwać do przodu do pojedynczego punktu czyszczenia wyjścia w funkcji. W naprawdę złożonych funkcjach rozluźniamy tę zasadę, aby umożliwić innym skok do przodu. W obu przypadkach unikamy głęboko zagnieżdżonych poleceń if, które często występują z sprawdzaniem kodu błędu, co ułatwia czytelność i konserwację.

 6
Author: Ari Pernick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-23 18:30:28

Najbardziej przemyślane i dogłębne omówienie twierdzeń goto, ich legalnych zastosowań i alternatywnych konstrukcji, które mogą być używane zamiast "cnotliwych twierdzeń goto", ale mogą być nadużywane tak łatwo, jak twierdzenia goto, jest artykuł Donalda Knutha " Structured Programming with Goto Statements", w grudniowych badaniach komputerowych 1974 (tom 6, nr 4. S. 261-301).

Nic dziwnego, że niektóre aspekty tej 39-letniej pracy są datowane: moc obliczeniowa sprawia, że niektóre ulepszenia wydajności Knutha są niezauważalne dla umiarkowanych problemów i od tego czasu wynaleziono nowe konstrukcje języka programowania. (Na przykład bloki try-catch poddają konstrukt Zahna, choć rzadko są w ten sposób używane.), Ale Knuth omawia wszystkie strony argumentacji i powinien być lekturą obowiązkową, zanim ktokolwiek ponownie poruszy tę kwestię.

 5
Author: nhcohen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-02 16:44:40

W module Perla od czasu do czasu chcesz tworzyć podprogramy lub zamknięcia w locie. Chodzi o to, że kiedy już stworzysz podprogram, jak do niego dotrzesz. Można to po prostu nazwać, ale jeśli podprogram używa caller() to nie będzie tak pomocne, jak mogłoby być. W tym miejscu goto &subroutine zmienność może być pomocna.

Oto krótki przykład:

sub AUTOLOAD{
  my($self) = @_;
  my $name = $AUTOLOAD;
  $name =~ s/.*:://;

  *{$name} = my($sub) = sub{
    # the body of the closure
  }

  goto $sub;

  # nothing after the goto will ever be executed.
}

Możesz również użyć tej formy goto, aby zapewnić podstawową formę wywołania ogonowego optymalizacja.

sub factorial($){
  my($n,$tally) = (@_,1);

  return $tally if $n <= 1;

  $tally *= $n--;
  @_ = ($n,$tally);
  goto &factorial;
}

( w Perl 5 Wersja 16 to byłoby lepiej napisane jako goto __SUB__; )

Istnieje moduł, który zaimportuje tail modyfikator i taki, który zaimportuje recur jeśli nie podoba Ci się korzystanie z tej formy goto.

use Sub::Call::Tail;
sub AUTOLOAD {
  ...
  tail &$sub( @_ );
}

use Sub::Call::Recur;
sub factorial($){
  my($n,$tally) = (@_,1);

  return $tally if $n <= 1;
  recur( $n-1, $tally * $n );
}

Większość innych powodów, aby użyć goto są lepiej zrobić z innymi słowami kluczowymi.

Jak redoing trochę kodu:

LABEL: ;
...
goto LABEL if $x;
{
  ...
  redo if $x;
}

Lub idąc do last z kawałka kodu z wielu miejsc:

goto LABEL if $x;
...
goto LABEL if $y;
...
LABEL: ;
{
  last if $x;
  ...
  last if $y
  ...
}
 3
Author: Brad Gilbert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55
Jeśli tak, to dlaczego?

C nie ma wielopoziomowego / znakowanego załamania i nie wszystkie przepływy kontrolne mogą być łatwo modelowane za pomocą iteracji C i pierwotnych decyzji. gotos idzie długą drogę w kierunku usunięcia tych wad.

Czasami jaśniej jest użyć jakiejś zmiennej flagowej do wywołania pseudo-wielopoziomowej przerwy, ale nie zawsze jest to lepsze niż goto (przynajmniej goto pozwala łatwo określić, do czego zmierza kontrola, w przeciwieństwie do zmiennej flagowej), a czasami można po prostu nie chcesz płacić ceny wydajności FLAG/innych konturów, aby uniknąć goto.

Libavcodec to wrażliwy na wydajność fragment kodu. Bezpośrednia ekspresja przepływu sterowania jest prawdopodobnie priorytetem, ponieważ będzie działać lepiej.

 2
Author: DrPizza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-23 19:14:36

Równie dobrze nikt nigdy nie wdrożył deklaracji "COME FROM"....

 2
Author: Ken Ray,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 16:48:35

Uważam użycie do {} while (false) za całkowicie odrażające. Jest to możliwe, może mnie przekonać, że jest to konieczne w jakimś dziwnym przypadku, ale nigdy, że jest to czysty rozsądny kod.

Jeśli musisz wykonać taką pętlę, dlaczego nie uczynić zależności od zmiennej flag jawnym?

for (stepfailed=0 ; ! stepfailed ; /*empty*/)
 2
Author: Sandy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-16 10:08:07

1) najczęstszym użyciem goto, o którym wiem, jest emulowanie obsługi wyjątków w językach, które go nie oferują, a mianowicie w C. (kod podany przez Nuclear powyżej jest właśnie tym.) Spójrz na kod źródłowy Linuksa, a zobaczysz bazylion gotos używanych w ten sposób; według szybkiej ankiety przeprowadzonej w 2013 roku w kodzie Linuksa było około 100 000 gotos: http://blog.regehr.org/archives/894 . użycie Goto jest nawet wspomniane w Linux coding style guide: https://www.kernel.org/doc/Documentation/CodingStyle . podobnie jak programowanie obiektowe jest emulowane za pomocą struktur wypełnionych wskaźnikami funkcyjnymi, goto ma swoje miejsce w programowaniu w języku C. Więc kto ma rację: Dijkstra czy Linus (i wszyscy koderzy jądra Linuksa)? Teoria kontra praktyka.

Jest jednak zwyczajowe, że nie ma wsparcia na poziomie kompilatora i sprawdza wspólne konstrukty/wzorce: łatwiej jest je źle używać i wprowadzać błędy bez kompilacji czeki. Windows i Visual C++, ale w trybie C oferują obsługę wyjątków przez SEH / VEH z tego właśnie powodu: wyjątki są przydatne nawet poza językami OOP, tj. w języku proceduralnym. Ale kompilator nie zawsze może zapisać swój boczek, nawet jeśli oferuje obsługę składniową WYJĄTKÓW w języku. Rozważ jako przykład tego ostatniego przypadku słynny błąd Apple SSL "goto fail", który właśnie zduplikował jednego goto z katastrofalnymi konsekwencjami ( https://www.imperialviolet.org/2014/02/22/applebug.html):

if (something())
  goto fail;
  goto fail; // copypasta bug
printf("Never reached\n");
fail:
  // control jumps here

Możesz mieć dokładnie ten sam błąd używając WYJĄTKÓW obsługiwanych przez kompilator, np. w C++:

struct Fail {};

try {
  if (something())
    throw Fail();
    throw Fail(); // copypasta bug
  printf("Never reached\n");
}
catch (Fail&) {
  // control jumps here
}

Ale obu wariantów błędu można uniknąć, jeśli kompilator analizuje i ostrzega o nieosiągalnym kodzie. Na przykład kompilacja z Visual C++ na poziomie Ostrzeżenia /W4 znajduje błąd w obu przypadkach. Java na przykład zabrania nieosiągalnego kodu (gdzie może go znaleźć!) z całkiem dobrego powodu: prawdopodobnie bądź pluskwą w kodzie przeciętnego Joe. Tak długo, jak konstrukcja goto nie pozwala na cele, które kompilator nie może łatwo zrozumieć, jak gotos do obliczania adresów (**), nie jest trudniej dla kompilatora znaleźć nieosiągalny kod wewnątrz funkcji z gotos niż używając kodu zatwierdzonego przez Dijkstrę.

(**) Przypisybibliografia Raczej myląco, w Fortranie "obliczone goto" odnosi się do konstrukcji jest to równoważne z instrukcją switch w C. Standard C nie zezwala na obliczanie gotos w języku, ale tylko gotos na deklarowane statycznie / składniowo etykiety. GNU C ma jednak rozszerzenie, które pozwala na uzyskanie adresu etykiety (operatora uniary, prefix&&), a także pozwala goto na zmienną typu void*. Zobacz https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html więcej na ten niejasny temat. Reszta tego posta Nie dotyczy tego niejasnego GNU C cecha.

Standardowe C (tzn. nie obliczane) Goto zazwyczaj nie są powodem, dla którego nieosiągalny kod nie może zostać znaleziony podczas kompilacji. Zwykle powodem jest kod logiczny, jak poniżej. Given

int computation1() {
  return 1;
}

int computation2() {
  return computation1();
}

Tak samo trudno jest kompilatorowi znaleźć nieosiągalny kod w którymkolwiek z następujących 3 konstruktów:

void tough1() {
  if (computation1() != computation2())
    printf("Unreachable\n");
}

void tough2() {
  if (computation1() == computation2())
    goto out;
  printf("Unreachable\n");
out:;
}

struct Out{};

void tough3() {
  try {
    if (computation1() == computation2())
      throw Out();
    printf("Unreachable\n");
  }
  catch (Out&) {
  }
}

(przepraszam za mój styl kodowania związany z klamrą, ale starałem się zachować przykłady jak najbardziej zwarte.)

Visual C++ / W4 (nawet z /Ox) nie znajduje nieosiągalnego kodu w każdy z nich, a jak zapewne wiesz, problem znalezienia kodu nieosiągalnego jest ogólnie nie do podjęcia decyzji. (Jeśli mi w to nie wierzysz: https://www.cl.cam.ac.uk/teaching/2006/OptComp/slides/lecture02.pdf )

Jako pokrewny problem, C goto może być używany do emulowania WYJĄTKÓW tylko wewnątrz ciała funkcji. Standardowa biblioteka C oferuje parę funkcji setjmp() i longjmp() do emulowania nielokalnych wyjść/wyjątków, ale mają one poważne wady w porównaniu z do tego, co oferują inne języki. Artykuł w Wikipedii http://en.wikipedia.org/wiki/Setjmp.h wyjaśnia dość dobrze tę drugą kwestię. Ta para funkcji działa również w systemie Windows (http://msdn.microsoft.com/en-us/library/yz2ez4as.aspx ), ale prawie nikt ich tam nie używa, ponieważ SEH / VEH jest lepszy. Nawet na Unix, myślę, że setjmp i longjmp są bardzo rzadko używane.

2) myślę, że drugim najczęstszym zastosowaniem goto w C jest implementacja wielopoziomowej przerwy lub wielopoziomowej kontynuacji, co jest również dość niekontrowersyjnym przypadkiem użycia. Przypomnij sobie, że środowisko Java nie zezwala na etykietę goto, ale pozwala na przerwanie etykiety lub kontynuowanie etykiety. Według http://www.oracle.com/technetwork/java/simple-142616.html , w rzeczywistości jest to najczęstszy przypadek użycia gotos w C( 90% mówią), ale z mojego subiektywnego doświadczenia, kod systemowy ma tendencję do częstszego używania gotos do obsługi błędów. Być może w kodzie naukowym lub tam, gdzie system operacyjny oferuje obsługę wyjątków (Windows), To wielopoziomowe wyjścia są dominujący przypadek użycia. Tak naprawdę nie podają żadnych szczegółów dotyczących kontekstu ich badania.

Edited to add: okazuje się, że te dwa wzorce użycia znajdują się w C book of Kernighan and Ritchie, około strony 60 (w zależności od wydania). Inną rzeczą jest to, że oba przypadki użycia dotyczą tylko forward gotos. I okazuje się, że MISRA C 2012 edition (w przeciwieństwie do edycji z 2004 roku) pozwala teraz na gotos, o ile są to tylko forward.

 2
Author: Fizz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-22 11:06:17

Niektórzy mówią, że nie ma powodu do goto w C++. Niektórzy twierdzą, że w 99% przypadków istnieją lepsze alternatywy. To nie jest rozumowanie, tylko irracjonalne wrażenia.Oto solidny przykład, w którym goto prowadzi do ładnego kodu, coś w rodzaju ulepszonej pętli do-while:]}

int i;

PROMPT_INSERT_NUMBER:
  std::cout << "insert number: ";
  std::cin >> i;
  if(std::cin.fail()) {
    std::cin.clear();
    std::cin.ignore(1000,'\n');
    goto PROMPT_INSERT_NUMBER;          
  }

std::cout << "your number is " << i;

Porównaj go z goto-free code:

int i;

bool loop;
do {
  loop = false;
  std::cout << "insert number: ";
  std::cin >> i;
  if(std::cin.fail()) {
    std::cin.clear();
    std::cin.ignore(1000,'\n');
    loop = true;          
  }
} while(loop);

std::cout << "your number is " << i;

Widzę te różnice:

  • zagnieżdżony {} blok jest potrzebny (choć do {...} while wygląda bardziej znajomo)
  • potrzebna jest dodatkowa zmienna loop, używana w czterech miejscach
  • [[22]}czytanie i zrozumienie pracy z loop
  • loop nie przechowuje żadnych danych, po prostu kontroluje przepływ wykonania, który jest mniej zrozumiały niż prosta Etykieta

Jest jeszcze jeden przykład

void sort(int* array, int length) {
SORT:
  for(int i=0; i<length-1; ++i) if(array[i]>array[i+1]) {
    swap(data[i], data[i+1]);
    goto SORT; // it is very easy to understand this code, right?
  }
}

A teraz pozbądźmy się" złego " goto:

void sort(int* array, int length) {
  bool seemslegit;
  do {
    seemslegit = true;
    for(int i=0; i<length-1; ++i) if(array[i]>array[i+1]) {
      swap(data[i], data[i+1]);
      seemslegit = false;
    }
  } while(!seemslegit);
}

Widzisz, że jest to ten sam typ użycia goto, jest to dobrze zorganizowany wzorzec i nie jest to forward goto tak wiele promocji, jak tylko zalecane sposób. Na pewno chcesz uniknąć takiego "inteligentnego" kodu:

void sort(int* array, int length) {
  for(int i=0; i<length-1; ++i) if(array[i]>array[i+1]) {
    swap(data[i], data[i+1]);
    i = -1; // it works, but WTF on the first glance
  }
}

Chodzi o to, że goto można łatwo nadużywać, ale sam goto nie jest winien. Należy zauważyć, że label ma zakres funkcji w C++, więc nie zanieczyszcza globalnego zakresu, jak w Pure assembly, w którym nakładające się pętle mają swoje miejsce i są bardzo powszechne - jak w poniższym kodzie dla 8051, gdzie wyświetlacz 7segment jest podłączony do P1. Program pętli lightning segment wokół:

; P1 states loops
; 11111110 <-
; 11111101  |
; 11111011  |
; 11110111  |
; 11101111  |
; 11011111  |
; |_________|

init_roll_state:
    MOV P1,#11111110b
    ACALL delay
next_roll_state:
    MOV A,P1
    RL A
    MOV P1,A
    ACALL delay
    JNB P1.5, init_roll_state
    SJMP next_roll_state

Jest jeszcze jeden zaleta: goto może służyć jako nazwane pętle, warunki i inne przepływy:

if(valid) {
  do { // while(loop)

// more than one page of code here
// so it is better to comment the meaning
// of the corresponding curly bracket

  } while(loop);
} // if(valid)

Lub możesz użyć równoważnego goto z wcięciem, więc nie musisz komentować, jeśli mądrze wybierzesz nazwę etykiety:

if(!valid) goto NOTVALID;
  LOOPBACK:

// more than one page of code here

  if(loop) goto LOOPBACK;
NOTVALID:;
 2
Author: Jan Turoň,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-16 10:10:31

W Perlu użycie etykiety do "goto" z pętli-użycie instrukcji "last", która jest podobna do break.

Pozwala to na lepszą kontrolę nad zagnieżdżonymi pętlami.

Tradycyjna Etykieta goto jest również obsługiwana, ale nie jestem pewien, czy jest zbyt wiele przypadków, w których jest to jedyny sposób na osiągnięcie tego, co chcesz - podprogramy i pętle powinny wystarczyć w większości przypadków.

 1
Author: Abhinav,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-23 18:22:08

Problem z 'goto' i najważniejszym argumentem ruchu 'goto-less programming' polega na tym, że jeśli używasz go zbyt często, Twój kod, chociaż może zachowywać się poprawnie, staje się nieczytelny, niemożliwy do utrzymania, nieczytelny itp. W 99,99% przypadków 'goto' prowadzi do kodu spaghetti. Osobiście nie mogę wymyślić żadnego dobrego powodu, dla którego miałbym użyć "goto".

 1
Author: cschol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-23 18:30:20

GOTO można oczywiście użyć, ale jest jeszcze jedna ważna rzecz niż styl kodu, lub jeśli kod jest czytelny lub nie, o czym musisz pamiętać, gdy go używasz: kod w środku może nie być tak solidny, jak myślisz .

Na przykład, spójrz na następujące dwa fragmenty kodu:

If A <> 0 Then A = 0 EndIf
Write("Value of A:" + A)

Równoważny kod z GOTO

If A == 0 Then GOTO FINAL EndIf
   A = 0
FINAL:
Write("Value of A:" + A)

Pierwszą rzeczą, o której myślimy, jest to, że wynikiem obu bitów kodu będzie "wartość A: 0" (Zakładamy, że wykonanie oczywiście bez paralelizmu)

To nie jest poprawne: w pierwszej próbce A zawsze będzie równe 0, ale w drugiej próbce (z instrukcją GOTO) A może nie być równe 0. Dlaczego?

Powodem jest to, że z innego punktu programu mogę wstawić GOTO FINAL bez kontrolowania wartości A.

Ten przykład jest bardzo oczywisty, ale gdy programy stają się coraz bardziej skomplikowane, trudność zobaczenia tego typu rzeczy wzrasta.

[[3]}podobne materiały można znaleźć w słynnym artykuł z Mr. Dijkstra "sprawa przeciwko GO to statement"
 1
Author: pocjoc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-06 17:25:59

Używam goto w następującym przypadku: w razie potrzeby należy wrócić z różnych miejsc, a przed powrotem należy wykonać pewną niewtajemniczenie:

Wersja bez goto:

int doSomething (struct my_complicated_stuff *ctx)    
{
    db_conn *conn;
    RSA *key;
    char *temp_data;
    conn = db_connect();  


    if (ctx->smth->needs_alloc) {
      temp_data=malloc(ctx->some_size);
      if (!temp_data) {
        db_disconnect(conn);
        return -1;      
        }
    }

    ...

    if (!ctx->smth->needs_to_be_processed) {
        free(temp_data);    
        db_disconnect(conn);    
        return -2;
    }

    pthread_mutex_lock(ctx->mutex);

    if (ctx->some_other_thing->error) {
        pthread_mutex_unlock(ctx->mutex);
        free(temp_data);
        db_disconnect(conn);        
        return -3;  
    }

    ...

    key=rsa_load_key(....);

    ...

    if (ctx->something_else->error) {
         rsa_free(key); 
         pthread_mutex_unlock(ctx->mutex);
         free(temp_data);
         db_disconnect(conn);       
         return -4;  
    }

    if (ctx->something_else->additional_check) {
         rsa_free(key); 
         pthread_mutex_unlock(ctx->mutex);
         free(temp_data);
         db_disconnect(conn);       
         return -5;  
    }


    pthread_mutex_unlock(ctx->mutex);
    free(temp_data);    
    db_disconnect(conn);    
    return 0;     
}

Wersja Goto:

int doSomething_goto (struct my_complicated_stuff *ctx)
{
    int ret=0;
    db_conn *conn;
    RSA *key;
    char *temp_data;
    conn = db_connect();  


    if (ctx->smth->needs_alloc) {
      temp_data=malloc(ctx->some_size);
      if (!temp_data) {
            ret=-1;
           goto exit_db;   
          }
    }

    ...

    if (!ctx->smth->needs_to_be_processed) {
        ret=-2;
        goto exit_freetmp;      
    }

    pthread_mutex_lock(ctx->mutex);

    if (ctx->some_other_thing->error) {
        ret=-3;
        goto exit;  
    }

    ...

    key=rsa_load_key(....);

    ...

    if (ctx->something_else->error) {
        ret=-4;
        goto exit_freekey; 
    }

    if (ctx->something_else->additional_check) {
        ret=-5;
        goto exit_freekey;  
    }

exit_freekey:
    rsa_free(key);
exit:    
    pthread_mutex_unlock(ctx->mutex);
exit_freetmp:
    free(temp_data);        
exit_db:
    db_disconnect(conn);    
    return ret;     
}

Druga wersja ułatwia, gdy trzeba coś zmienić w poleceniach deallocation (każda z nich jest używana raz w kodzie), oraz zmniejsza szansę na pominięcie którejkolwiek z nich przy dodawaniu nowej gałęzi. Przenoszenie ich w funkcji tutaj nie pomoże, ponieważ dealokacja może odbywać się na różnych "poziomach".

 1
Author: Nuclear,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-19 20:45:45

Edsger Dijkstra, informatyk, który miał duży wkład w tej dziedzinie, był również znany z krytykowania użycia GoTo. Jest krótki artykuł o jego argumentacji na Wikipedii.

 0
Author: bubbassauro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-25 03:44:23

Przydaje się od czasu do czasu do przetwarzania łańcuchów znaków.

Wyobraź sobie coś takiego jak ten printf-esque przykład:

for cur_char, next_char in sliding_window(input_string) {
    if cur_char == '%' {
        if next_char == '%' {
            cur_char_index += 1
            goto handle_literal
        }
        # Some additional logic
        if chars_should_be_handled_literally() {
            goto handle_literal
        }
        # Handle the format
    }
    # some other control characters
    else {
      handle_literal:
        # Complicated logic here
        # Maybe it's writing to an array for some OpenGL calls later or something,
        # all while modifying a bunch of local variables declared outside the loop
    }
}

Możesz przekształcić to goto handle_literal w wywołanie funkcji, ale jeśli modyfikuje ona kilka różnych zmiennych lokalnych, musisz przekazać odwołania do każdej z nich, chyba że twój język obsługuje mutable closures. Nadal będziesz musiał użyć continue instrukcji (która jest prawdopodobnie formą goto) po wywołaniu, aby uzyskać tę samą semantykę, jeśli twoja logika tworzy else sprawa nie działa.

Używałem również gotos rozsądnie w lekserach, zazwyczaj w podobnych przypadkach. Nie potrzebujesz ich przez większość czasu, ale są miłe dla tych dziwnych spraw.

 0
Author: Beefster,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-07-30 23:11:12

Natknąłem się na sytuację, w której goto było dobrym rozwiązaniem. I nie widziałem tego przykładu tutaj ani nigdzie.

Miałem skrzynkę przełącznika z kilkoma skrzynkami, które w końcu wymagały wywołania tej samej funkcji. Miałem inne przypadki, które w końcu wymagały wywołania innej funkcji.

To wyglądało trochę tak:

switch( x ) {
    
    case 1: case1() ; doStuffFor123() ; break ;
    case 2: case2() ; doStuffFor123() ; break ;
    case 3: case3() ; doStuffFor123() ; break ;
    
    case 4: case4() ; doStuffFor456() ; break ;
    case 5: case5() ; doStuffFor456() ; break ;
    case 6: case6() ; doStuffFor456() ; break ;
    
    case 7: case7() ; doStuffFor789() ; break ;
    case 8: case8() ; doStuffFor789() ; break ;
    case 9: case9() ; doStuffFor789() ; break ;
}

Zamiast dawać każdemu Przypadkowi wywołanie funkcji, zamieniłem {[3] } na goto. goto przeskakuje do etykiety, która również znajduje się wewnątrz przełącznika case.

switch( x ) {
    
    case 1: case1() ; goto stuff123 ;
    case 2: case2() ; goto stuff123 ;
    case 3: case3() ; goto stuff123 ;
    
    case 4: case4() ; goto stuff456 ;
    case 5: case5() ; goto stuff456 ;
    case 6: case6() ; goto stuff456 ;
    
    case 7: case7() ; goto stuff789 ;
    case 8: case8() ; goto stuff789 ;
    case 9: case9() ; goto stuff789 ;
    
    stuff123: doStuffFor123() ; break ;
    stuff456: doStuffFor456() ; break ;
    stuff789: doStuffFor789() ; break ;
}

Przypadki od 1 do 3 wszystkie muszą zadzwonić doStuffFor123() i podobnie przypadki od 4 do 6 musiały zadzwonić doStuffFor456() itd.

IMO gotos są w porządku, jeśli dobrze ich używasz. W końcu każdy kod jest tak jasny, jak ludzie go piszą. Z gotos można zrobić kod spaghetti, ale to nie znaczy, że gotos są przyczyną kodu spaghetti. To my, programiści. Mogę również utworzyć kod spaghetti Z FUNKCJAMI, jeśli chcę. To samo dotyczy również makr
 0
Author: bask185,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-26 10:34:03