Przykłady dobrych gotos w C lub C++ [zamknięty]

W tym wątku przyjrzymy się przykładom dobrych zastosowań goto w C lub c++. Zainspirowana jest odpowiedzią, którą głosowali ludzie, bo myśleli, że żartuję.

Podsumowanie (Etykieta zmieniona z oryginału, aby intencja była jeszcze jaśniejsza):

infinite_loop:

    // code goes here

goto infinite_loop;

Dlaczego jest lepszy od alternatyw:

    Jest specyficzny. goto jest konstrukcji języka, która powoduje oddział bezwarunkowy. Alternatywy zależy od wykorzystania struktur wsparcie warunkowe oddziały, z degeneratem zawsze-prawda warunek.
  • Etykieta dokumentuje zamiar bez dodatkowych komentarzy.
  • czytelnik nie musi skanować kod interweniujący na początku break s (chociaż nadal jest to możliwe dla bezpodstawny haker do symulacji continue z wczesnym goto).

Zasady:

  • udawać, że gotofoby nie wygraj. Zrozumiałe jest, że powyższe nie może być użyty w prawdziwym kodzie, ponieważ to wbrew założeniom idiom.
  • Załóżmy, że wszyscy słyszeliśmy o "Goto uważane za szkodliwe" i wiedzieć że goto można wykorzystać do napisania Kodeks spaghetti.
  • Jeśli nie zgadzasz się z przykładem, / align = "left" / alone ('bo ludzie nie lubią goto ' nie jest powodem technicznym).
[16]}zobaczmy, czy możemy porozmawiać o tym jak dorośli.

Edit

To pytanie wydaje się już skończone. To wygenerowało kilka wysokiej jakości odpowiedzi. Podziękowania dla wszystkich, zwłaszcza tych, którzy wzięli mój mały przykład z pętli na poważnie. Większość sceptyków była zaniepokojona przez brak zasięgu bloku. Jak zauważył @quinmars w komentarzu, zawsze można założyć aparat ortodontyczny na loop body. Zauważam na marginesie, że for(;;) i while(true) nie dają ci aparatu za darmo (a pominięcie ich może spowodować irytujące błędy). W każdym razie, nie będę marnować więcej mocy twojego mózgu na tym drobiazgu-mogę żyć z nieszkodliwym i idiomatycznym for(;;) i while(true) (równie dobrze, jeśli chcę zachować moją Hiob).

Biorąc pod uwagę inne odpowiedzi, widzę, że wiele osób postrzega goto jako coś, co zawsze trzeba przepisać w inny sposób. Oczywiście można uniknąć goto wprowadzając pętlę, dodatkowy znacznik, stos zagnieżdżonych if s, czy cokolwiek innego, ale dlaczego nie rozważyć, czy goto jest być może najlepsze narzędzie do pracy? Innymi słowy, ile brzydoty ludzie są gotowi znieść, aby uniknąć używania wbudowanej funkcji języka zgodnie z jej przeznaczeniem? Moim zdaniem to nawet dodając flaga to zbyt wysoka Cena. Lubię, gdy moje zmienne reprezentują rzeczy w domeny problemu lub rozwiązania. / Align = "left" /

Przyjmę pierwszą odpowiedź, która dała wzór C do rozgałęzienia do bloku czyszczenia. IMO to najmocniej uzasadnia goto ze wszystkich zamieszczonych odpowiedzi, na pewno jeśli mierzysz je po kontorach, przez które hejter musi przejść, aby tego uniknąć.

 79
Author: Community, 2008-10-29

16 answers

Oto jedna sztuczka, o której słyszałem. Nigdy nie widziałem go w dziczy. I dotyczy tylko C, ponieważ C++ ma RAII, aby zrobić to bardziej idiomatycznie.

void foo()
{
    if (!doA())
        goto exit;
    if (!doB())
        goto cleanupA;
    if (!doC())
        goto cleanupB;

    /* everything has succeeded */
    return;

cleanupB:
    undoB();
cleanupA:
    undoA();
exit:
    return;
}
 87
Author: Greg Rogers,
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-02-11 04:00:48

Klasyczna potrzeba GOTO w C jest następująca

for ...
  for ...
    if(breakout_condition) 
      goto final;

final:

Nie ma prostego sposobu na wyrwanie się z zagnieżdżonych pętli bez goto.

 85
Author: Paul Nathan,
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-10-29 04:31:43

Oto Mój nie-głupi przykład, (od Stevens APITUE) dla wywołań systemu Unix, które mogą być przerwane przez sygnał.

restart:
    if (system_call() == -1) {
        if (errno == EINTR) goto restart;

        // handle real errors
    }

Alternatywą jest pętla zdegenerowana. Ta wersja brzmi jak angielski "jeśli wywołanie systemowe zostało przerwane przez sygnał, uruchom je ponownie".

 32
Author: fizzer,
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
2011-04-23 10:57:51

Jeśli urządzenie Duffa nie potrzebuje goto, to Ty też nie powinieneś! ;)

void dsend(int count) {
    int n;
    if (!count) return;
    n = (count + 7) / 8;
    switch (count % 8) {
      case 0: do { puts("case 0");
      case 7:      puts("case 7");
      case 6:      puts("case 6");
      case 5:      puts("case 5");
      case 4:      puts("case 4");
      case 3:      puts("case 3");
      case 2:      puts("case 2");
      case 1:      puts("case 1");
                 } while (--n > 0);
    }
}

Powyższy kod z Wikipedii wpis.

 14
Author: Mitch Wheat,
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-10-29 04:08:23

Knuth napisał referat "programowanie strukturalne z poleceniami GOTO", można go pobrać np. z tutaj . Znajdziesz tam wiele przykładów.

 14
Author: zvrba,
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-10-29 07:17:10

Bardzo często.

do_stuff(thingy) {
    lock(thingy);

    foo;
    if (foo failed) {
        status = -EFOO;
        goto OUT;
    }

    bar;
    if (bar failed) {
        status = -EBAR;
        goto OUT;
    }

    do_stuff_to(thingy);

OUT:
    unlock(thingy);
    return status;
}

Jedyny przypadek, którego używam goto to skakanie do przodu, zwykle z bloków, a nigdy do bloków. Pozwala to uniknąć nadużywania do{}while(0) i innych konstrukcji, które zwiększają zagnieżdżanie, zachowując jednocześnie czytelny, ustrukturyzowany kod.

 12
Author: ephemient,
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-10-29 04:18:02

Nie mam nic przeciwko gotos w ogóle, ale mogę wymyślić kilka powodów, dla których nie chcesz ich używać do pętli, jak wspomniałeś:

  • nie ogranicza zakresu, dlatego zmienne temp, których używasz, zostaną zwolnione dopiero później.
  • nie ogranicza zasięgu, dlatego może prowadzić do błędów.
  • nie ogranicza zakresu, dlatego nie można ponownie użyć tych samych nazw zmiennych później w przyszłym kodzie w tym samym zakresie.
  • nie ogranicza zakresu stąd masz możliwość pominięcia deklaracji zmiennej.
  • ludzie nie są do tego przyzwyczajeni, a to sprawi, że Twój kod będzie trudniejszy do odczytania.
  • zagnieżdżone pętle tego typu mogą prowadzić do kodu spaghetti, normalne pętle nie prowadzą do kodu spaghetti.
 12
Author: Brian R. Bondy,
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
2011-04-23 10:57:28

Dobrym miejscem do użycia Goto jest procedura, która może zostać przerwana w kilku punktach, z których każdy wymaga różnych poziomów czyszczenia. Gotophobes zawsze może zastąpić gotos ustrukturyzowanym kodem i serią testów, ale myślę, że jest to prostsze, ponieważ eliminuje nadmierne wcięcie:

if (!openDataFile())
  goto quit;

if (!getDataFromFile())
  goto closeFileAndQuit;

if (!allocateSomeResources)
  goto freeResourcesAndQuit;

// Do more work here....

freeResourcesAndQuit:
   // free resources
closeFileAndQuit:
   // close file
quit:
   // quit!
 7
Author: Adam Liss,
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-10-29 04:09:17

@fizzer.myopenid.com: Twój opublikowany fragment kodu jest odpowiednikiem następującego:

    while (system_call() == -1)
    {
        if (errno != EINTR)
        {
            // handle real errors

            break;
        }
    }
Zdecydowanie wolę tę formę.
 7
Author: Mitch Wheat,
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-10-29 04:35:48

Mimo, że z czasem znienawidziłem ten wzorzec, to jest on wkomponowany w programowanie COM.

#define IfFailGo(x) {hr = (x); if (FAILED(hr)) goto Error}
...
HRESULT SomeMethod(IFoo* pFoo) {
  HRESULT hr = S_OK;
  IfFailGo( pFoo->PerformAction() );
  IfFailGo( pFoo->SomeOtherAction() );
Error:
  return hr;
}
 6
Author: JaredPar,
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-10-29 06:49:36

Oto przykład dobrego goto:

// No Code
 5
Author: FlySwat,
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-10-29 04:01:13

Widziałem goto używane poprawnie, ale sytuacje są normalnie brzydkie. Tylko wtedy, gdy samo użycie goto jest o wiele mniej gorsze od oryginału. @ Johnathon Holland The poblem is you ' re version is less clear. ludzie zdają się bać lokalnych zmiennych:

void foo()
{
    bool doAsuccess = doA();
    bool doBsuccess = doAsuccess && doB();
    bool doCsuccess = doBsuccess && doC();

    if (!doCsuccess)
    {
        if (doBsuccess)
            undoB();
        if (doAsuccess)
            undoA();
    }
}

I wolę takie pętle, ale niektórzy wolą while(true).

for (;;)
{
    //code goes here
}
 1
Author: Charles Beattie,
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
2011-01-05 19:29:57

Mój problem z tym polega na tym, że tracisz zasięg bloku; wszelkie zmienne lokalne zadeklarowane między gotos pozostają w mocy, jeśli pętla zostanie kiedykolwiek zerwana. (Może zakładasz, że pętla działa w nieskończoność; nie sądzę, że to jest to, co autor pytania zadawał, chociaż.)

Problem scopowania jest bardziej problemem w C++, ponieważ niektóre obiekty mogą zależeć od wywoływania ich dtor w odpowiednim czasie.

Dla mnie najlepszym powodem do użycia goto jest podczas wieloetapowego proces inicjalizacji, w którym ważne jest, aby wszystkie Inity były wycofane, jeśli jeden się nie powiedzie, a la:

if(!foo_init())
  goto bye;

if(!bar_init())
  goto foo_bye;

if(!xyzzy_init())
  goto bar_bye;

return TRUE;

bar_bye:
 bar_terminate();

foo_bye:
  foo_terminate();

bye:
  return FALSE;
 0
Author: Jim Nelson,
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-10-29 04:11:44

Sam nie używam goto, jednak pracowałem kiedyś z osobą, która używałaby ich w konkretnych przypadkach. O ile dobrze pamiętam, jego uzasadnienie dotyczyło kwestii wydajności - miał też określone zasady Jak. Zawsze w tej samej funkcji, a etykieta była zawsze pod instrukcją goto.

 0
Author: user25306,
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-10-29 05:53:48
#include <stdio.h>
#include <string.h>

int main()
{
    char name[64];
    char url[80]; /*The final url name with http://www..com*/
    char *pName;
    int x;

    pName = name;

    INPUT:
    printf("\nWrite the name of a web page (Without www, http, .com) ");
    gets(name);

    for(x=0;x<=(strlen(name));x++)
        if(*(pName+0) == '\0' || *(pName+x) == ' ')
        {
            printf("Name blank or with spaces!");
            getch();
            system("cls");
            goto INPUT;
        }

    strcpy(url,"http://www.");
    strcat(url,name);
    strcat(url,".com");

    printf("%s",url);
    return(0);
}
 0
Author: StrifeSephiroth,
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-10 12:59:14

@ Greg:

Dlaczego nie zrobić przykład w ten sposób:

void foo()
{
    if (doA())
    {    
        if (doB())
        {
          if (!doC())
          {
             UndoA();
             UndoB();
          }
        }
        else
        {
          UndoA();
        }
    }
    return;
}
 -1
Author: FlySwat,
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-10-29 04:37:36