Frage

In diesem Thread betrachten wir Beispiele für eine gute Verwendung von goto in C oder C ++. Es wird von eine Antwort die Menschen gestimmt, weil sie dachte, ich war ein Witz.

Zusammenfassung (Label geändert von den ursprünglichen Absicht noch deutlicher zu machen):

infinite_loop:

    // code goes here

goto infinite_loop;

Warum ist es besser als die Alternativen:

  • Es ist spezifisch. goto ist die Sprachkonstrukt, das ein verursacht unbedingte Verzweigung. Alternativen hängen von Strukturen unter Verwendung von Stütz bedingte Verzweigungen, mit einem degenerierten immer wahr Zustand.
  • Das Label dokumentiert die Absicht ohne zusätzliche Kommentare.
  • Der Leser muss nicht scannen die intervenierenden Code für die frühe breaks (Obwohl es ist immer noch möglich, dass ein charakterlos Hacker zu simulieren continue mit einem frühen goto).

Regeln:

  • Pretend, dass der gotophobes nicht tat Sieg. Es versteht sich, dass die oben kann nicht in echtem Code verwendet werden, da es geht gegen etabliertes Idiom.
  • Nehmen wir an, wir haben alle gehört, von ‚Goto als schädlich‘ und weiß, dass goto kann verwendet werden, um zu schreiben Spaghetti-Code.
  • Wenn Sie mit einem Beispiel nicht einverstanden ist, kritisieren sie auf den technischen Wert allein ( "Weil die Menschen nicht wie goto‘ist kein technischer Grund).

Lassen Sie uns sehen, ob wir über diese wie Erwachsenen sprechen kann.

Bearbeiten

Diese Frage scheint nun beendet. Es erzeugt einige hochwertige Antworten. Danke an alle,  insbesondere diejenigen, die ernsthaft meine kleine Schleife Beispiel nahm. Die meisten Skeptiker waren besorgt  durch das Fehlen von Block Umfang. Wie @quinmars in einem Kommentar darauf hingewiesen, können Sie immer Klammern um das setzen Schleifenkörper. Ich stelle fest, beiläufig, dass for(;;) und while(true) geben Sie nicht die Klammern kostenlos entweder (und Weglassen kann sie ärgerliche Fehler verursachen). Wie auch immer, ich werde nicht mehr verschwenden Ihr Gehirn Macht auf dieser Kleinigkeit - ich kann mit dem harmlosen und idiomatischen for(;;) und while(true) (genauso gut, wenn ich meinen Job behalten will) leben.

die anderen Antworten Betrachtet man, wie ich sehe, dass viele Menschen goto als etwas sehen Sie immer haben in einer anderen Art und Weise neu zu schreiben. Natürlich können Sie eine goto durch die Einführung einer Schleife vermeiden,  ein zusätzliches Flag, ein Stapel von verschachtelten ifs, oder was auch immer, aber warum nicht prüfen, ob goto ist vielleicht das beste Werkzeug für den Job? Um es anders auszudrücken, wie viel Hässlichkeit sind Menschen bereit zu ertragen eine eingebaute Sprache-Funktion für den beabsichtigten Zweck zu vermeiden, mit? Meine Meinung ist, dass auch ein Flag hinzuzufügen, ist ein zu hoher Preis zu zahlen. Ich mag meine Variablen darstellen Dinge in das Problem oder die Lösung Domänen. ‚Einzig und allein einen goto zu vermeiden‘ es nicht schneiden.

Ich werde die erste Antwort akzeptieren, die zur Verzweigung zu einem Bereinigungsblock des C-Muster gab. IMO, macht dies das stärkste Argument für ein goto alle geschrieben Antworten, sicherlich wenn Sie es von den Verrenkungen ein Hasser durch sie vermeiden, gehen muss messen.

War es hilfreich?

Lösung

Heres ein Trick, den ich habe mit den Menschen gehört. Ich habe es allerdings nie in freier Natur gesehen. Und es gilt nur für C, da C ++ hat RAII dies mehr idiomatisch zu tun.

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

    /* everything has succeeded */
    return;

cleanupB:
    undoB();
cleanupA:
    undoA();
exit:
    return;
}

Andere Tipps

Die klassische Notwendigkeit GOTO in C wird wie folgt

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

final:

Es gibt keine einfache Möglichkeit, ohne goto aus verschachtelten Schleifen zu brechen.

Hier ist mein nicht dumm Beispiel (von Stevens APITUE) für Unix-Systemaufrufe, die durch ein Signal unterbrochen werden können.

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

        // handle real errors
    }

Die Alternative ist eine degenerierte Schleife. Diese Version liest sich wie Englisch „wenn der Systemaufruf durch ein Signal unterbrochen wurde, starten Sie es“.

Wenn Duff Gerät keine goto benötigen, dann sollten weder Sie! ;)

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);
    }
}

Code oben von Wikipedia Eintrag .

Knuth ein Papier „Strukturierte Programmierung mit GOTO-Anweisungen“ geschrieben hat, können Sie es zum Beispiel von hier . Sie finden es viele Beispiele finden.

Sehr häufig.

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;
}

Der einzige Fall, den ich je goto verwenden ist nach vorn für das Springen, in der Regel aus den Blöcken, und nie in Blöcke. Dies vermeidet Missbrauch von do{}while(0) und anderen Konstrukten, die Verschachtelung erhöhen, während noch lesbar, strukturierten Code beibehalten wird.

Ich habe nichts gegen gotos im Allgemeinen, aber ich kann aus mehreren Gründen ein, warum man sie nicht für eine Schleife verwenden möchte, wie Sie erwähnt haben:

  • Es schränkt Umfang daher keine temporären Variablen, die Sie im Inneren verwenden wird erst später befreit werden.
  • Es schränkt Umfang daher könnte es zu Fehlern führen.
  • Es schränkt Umfang daher können Sie nicht die gleichen Variablennamen später in Zukunft Code im gleichen Umfang wiederverwenden.
  • Es schränkt Umfang damit Sie die Chance des Überspringens über eine Variablendeklaration haben.
  • sind nicht daran gewöhnt, es Menschen und es wird Ihr Code schwerer lesbar machen.
  • Nested Loops diese Art Spaghetti-Code führen kann, Normale Schleifen nicht zu Spaghetti-Code führen.

Ein guter Ort, um eine goto zu verwenden, ist in einem Verfahren, das an mehreren Stellen abbrechen können, von denen jede verschiedene Ebenen der Bereinigung erfordert. Gotophobes kann immer den gotos mit strukturiertem Code und eine Reihe von Tests ersetzen, aber ich denke, das einfacher ist, weil es übermäßige Vertiefung beseitigt:

if (!openDataFile())
  goto quit;

if (!getDataFromFile())
  goto closeFileAndQuit;

if (!allocateSomeResources)
  goto freeResourcesAndQuit;

// Do more work here....

freeResourcesAndQuit:
   // free resources
closeFileAndQuit:
   // close file
quit:
   // quit!

@ fizzer.myopenid.com: geposteten Code-Schnipsel entspricht dem folgenden:

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

            break;
        }
    }

ich auf jeden Fall diese Form bevorzugen.

Auch wenn ich dieses Muster im Laufe der Zeit zu hassen gewachsen sind, ist es in körnig in COM-Programmierung.

#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;
}

Hier ist ein Beispiel für eine gute goto:

// No Code

Ich habe goto richtig verwendet gesehen, aber die Situationen sind normalerweise hässlich. Erst dann, wenn die Verwendung von goto selbst so viel weniger ist schlechter als das Original. @Johnathon Holland die poblem ist, den Sie Version ist weniger klar. Leute scheinen Angst von lokalen Variablen werden:

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

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

Und ich ziehe Schleifen wie diese, aber einige Leute bevorzugen while(true).

for (;;)
{
    //code goes here
}

Meine meckern daran ist, dass Sie Block Scoping verlieren; alle lokalen Variablen zwischen dem GOTOs bleiben in Kraft erklärt, wenn die Schleife je aus gebrochen. (Vielleicht sind Sie die Schleife laufen unter der Annahme, für immer;. Ich glaube nicht, dass das, was die ursprüngliche Frage Schreiber fragt, obwohl)

Das Problem des Scoping ist eher ein Problem mit C ++, da einige Objekte auf ihrem dtor Abhängigkeit können zu geeigneten Zeiten aufgerufen werden.

Für mich der beste Grund, gehe zu verwenden, ist während eines mehrstufigen Initialisierung, wo der es ist wichtig, dass alle inits gesichert aus, wenn einer ausfällt, 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;

ich mich nicht GOTOs, jedoch habe ich die Arbeit mit einer Person einmal, dass sie in bestimmten Fällen verwenden würde. Wenn ich mich richtig erinnere, seine Gründe um Performance-Probleme war - er hatte auch spezielle Regeln für wie . Immer in der gleichen Funktion, und das Etikett war immer unterhalb der goto-Anweisung.

#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);
}

@ Greg:

Warum nicht Ihr Beispiel wie folgt aus:

void foo()
{
    if (doA())
    {    
        if (doB())
        {
          if (!doC())
          {
             UndoA();
             UndoB();
          }
        }
        else
        {
          UndoA();
        }
    }
    return;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top