it-swarm.com.de

Beispiele für gute gotos in C oder C++

In diesem Thread betrachten wir Beispiele für gute Verwendungen von goto in C oder C++. Es ist inspiriert von einer Antwort , die die Leute gewählt haben, weil sie dachten, ich würde Spaß machen.

Zusammenfassung (Etikett wurde vom Original geändert, um die Absicht noch klarer zu machen):

infinite_loop:

    // code goes here

goto infinite_loop;

Warum ist es besser als die Alternativen:

  • Es ist spezifisch. goto ist das Sprachkonstrukt .__, das einen Unbedingten Zweig verursacht. Die Alternativen Hängen von der Verwendung von Strukturen Ab, die bedingte Verzweigungen unterstützen, Mit einer degenerierten immerwahrhaften Bedingung.
  • Das Etikett dokumentiert die Absicht .__ ohne zusätzliche Kommentare.
  • Der Leser muss den -Interventionscode nicht auf frühe breaks -Dateien scannen (obwohl es immer noch möglich ist, dass ein Unprincipled Hacker continue mit einem frühen goto simuliert).

Regeln:

  • Tun Sie so, als hätten die Gotophobes nicht Gewonnen. Es versteht sich, dass das obige Nicht in echtem Code verwendet werden kann, weilit gegen etablierte Idiome verstößt.
  • Angenommen, wir haben alle von .__ gehört. "Goto als schädlich betrachtet" und wissen .__, dass goto zum Schreiben von Spaghetti-Code verwendet werden kann.
  • Wenn Sie mit einem Beispiel nicht einverstanden sind, kritisieren Es allein aus technischen Gründen. ('Weil die Leute nicht mögen Goto' ist kein technischer Grund).

Mal sehen, ob wir wie Erwachsene darüber reden können.

Bearbeiten

Diese Frage scheint jetzt beendet zu sein. Es wurden einige qualitativ hochwertige Antworten generiert. Danke an alle, vor allem diejenigen, die mein kleines Schleifenbeispiel ernst genommen haben. Die meisten Skeptiker waren betroffen. durch den fehlenden Blockumfang. Wie @quinmars in einem Kommentar hervorgehoben hat, können Sie immer Klammern um den Schleifenkörper setzen. Ich stelle fest, dass for(;;) und while(true) Ihnen die Klammern Nicht kostenlos zur Verfügung stellen (und das Weglassen von ihnen kann ärgerliche Fehler verursachen). Jedenfalls vergeude ich nichts mehr von Ihrer Gehirnleistung mit dieser Kleinigkeit - ich kann mit den harmlosen und idiomatischen for(;;) und while(true) leben (genauso gut, wenn ich meinen Job behalten möchte). 

In Anbetracht der anderen Antworten sehe ich, dass viele Leute goto als etwas betrachten, das Sie immer Auf eine andere Weise umschreiben müssen. Natürlich können Sie ein goto vermeiden, indem Sie eine Schleife .__ einführen. ein zusätzliches Flag, ein Stapel verschachtelter ifs oder was auch immer, aber warum nicht erwägen, ob goto .__ das vielleicht beste Werkzeug für den Job ist? Anders ausgedrückt, wie viel Hässlichkeit sind die Menschen bereit, zu ertragen, um zu vermeiden, dass eine eingebaute Sprachfunktion für ihren beabsichtigten Zweck verwendet wird? Meiner Meinung nach ist Selbst das Hinzufügen einer Flagge ist ein zu hoher Preis. Ich mag es, dass meine Variablen die Dinge in den Problem- oder Lösungsdomänen darstellen. 'Nur um ein goto zu vermeiden' schneidet es nicht ab.

Ich akzeptiere die erste Antwort, die das C-Muster für die Verzweigung in einen Bereinigungsblock gegeben hat. IMO, das ist der beste Fall für eine goto aller geposteten Antworten, sicherlich Wenn Sie es an den Verwerfungen messen, die ein Hasser durchmachen muss, um sie zu vermeiden.

79
fizzer

Hier ist ein Trick, den ich von Leuten gehört habe. Ich habe es noch nie in freier Wildbahn gesehen. Und dies gilt nur für C, da C++ über RAII verfügt, um dies idiomatischer zu machen. 

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
Greg Rogers

Das klassische Bedürfnis nach GOTO in C ist wie folgt

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

final:

Es gibt keinen einfachen Weg, um geschachtelte Schleifen ohne Umstieg zu durchbrechen.

85
Paul Nathan

Hier ist mein nicht dummes 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 entartete Schleife. Diese Version liest sich wie in Englisch "Wenn der Systemaufruf durch ein Signal unterbrochen wurde, starten Sie ihn neu".

32
fizzer

Wenn das Gerät von Duff keinen Goto braucht, sollten Sie auch nicht! ;)

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 aus Wikipedia Eintrag .

14
Mitch Wheat

Knuth hat einen Aufsatz "Strukturierte Programmierung mit GOTO-Anweisungen" verfasst. von hier . Sie finden dort viele Beispiele.

14
zvrba

Sehr gewöhnlich.

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 jemals goto benutze, ist das Springen nach vorne, normalerweise aus Blöcken, und niemals in Blöcke. Dies vermeidet den Missbrauch von do{}while(0) und anderen Konstrukten, die die Verschachtelung erhöhen, während lesbarer, strukturierter Code erhalten bleibt.

12
ephemient

Ich habe nichts gegen Gotos im Allgemeinen, aber mir fallen mehrere Gründe ein, warum Sie sie nicht für eine Schleife wie Sie verwenden möchten:

  • Der Umfang wird dadurch nicht eingeschränkt, daher werden temporäre Variablen, die Sie innen verwenden, erst später freigegeben.
  • Es beschränkt den Umfang nicht und kann daher zu Fehlern führen.
  • Es beschränkt den Gültigkeitsbereich nicht, daher können Sie die gleichen Variablennamen später in künftigem Code nicht mehr im selben Gültigkeitsbereich verwenden. 
  • Der Umfang wird nicht eingeschränkt, daher besteht die Möglichkeit, eine Variablendeklaration zu überspringen.
  • Die Leute sind nicht daran gewöhnt und es wird schwieriger, Ihren Code zu lesen.
  • Verschachtelte Schleifen dieses Typs können zu Spaghetti-Code führen, normale Loops führen nicht zu Spaghetti-Code.
12
Brian R. Bondy

Ein guter Ort für die Verwendung eines goto ist ein Verfahren, das an mehreren Stellen abgebrochen werden kann, von denen jede verschiedene Reinigungsstufen erfordert. Gotophobes können die gotos immer durch strukturierten Code und eine Reihe von Tests ersetzen, aber ich denke, das ist einfacher, da übermäßige Einrückungen vermieden werden:

 if (! openDataFile ()) 
 goto quit; 

 if (! getDataFromFile ()) 
 gehe zu closeFileAndQuit; 

 if (! AllocateSomeResources) 
 goto freeResourcesAndQuit; 

 // Hier mehr Arbeit erledigen ....

 freeResourcesAndQuit: 
 // freie Ressourcen 
 closeFileAndQuit: 
 // Schließen Sie die Datei 
 quit: 
 // Verlassen!
7
Adam Liss

@ fizzer.myopenid.com: Ihr geposteter Code-Snippet entspricht dem Folgenden:

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

            break;
        }
    }

Ich bevorzuge definitiv diese Form.

7
Mitch Wheat

Auch wenn ich dieses Muster mit der Zeit hassen kann, ist es in die COM-Programmierung eingebettet. 

#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
JaredPar

Hier ist ein Beispiel für einen guten goto:

// No Code
5
FlySwat

Ich habe gesehen, dass man richtig benutzt wurde, aber die Situationen sind normalerweise hässlich. Nur wenn die Verwendung von goto selbst so viel schlechter ist als das Original . @ Johnathon Holland ist das Problem, dass Ihre Version weniger klar ist. Menschen scheinen Angst vor lokalen Variablen zu haben:

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

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

Und ich bevorzuge solche Loops, aber manche Leute bevorzugen while(true).

for (;;)
{
    //code goes here
}
1
Charles Beattie

Mein Kritikpunkt dabei ist, dass Sie das Block-Scoping verlieren. Alle lokalen Variablen, die zwischen den gotos deklariert wurden, bleiben in Kraft, wenn die Schleife jemals ausgebrochen ist. (Vielleicht gehen Sie davon aus, dass die Schleife für immer läuft; ich glaube jedoch nicht, dass dies der ursprüngliche Fragesteller der Frage war.)

Das Problem des Bereichs ist in C++ eher ein Problem, da einige Objekte davon abhängen, ob sie zu geeigneten Zeitpunkten aufgerufen werden.

Für mich ist der beste Grund, goto zu verwenden, während eines mehrstufigen Initialisierungsprozesses, bei dem es unabdingbar ist, dass alle Inits zurückgesetzt werden, falls einer ausfällt.

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
Jim Nelson
#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
StrifeSephiroth

Ich benutze goto's nicht selbst, aber ich habe einmal mit einer Person gearbeitet, die sie in bestimmten Fällen verwenden würde. Wenn ich mich recht erinnere, ging es um Performance-Probleme - er hatte auch spezifische Regeln für how . Immer in der gleichen Funktion, und das Label war immer unter der goto-Anweisung.

0
user25306