In diesem Thread sehen wir uns Beispiele für gute Verwendungen goto
in C oder C ++ an. Es ist inspiriert von einer Antwort, die die Leute gewählt haben, weil sie dachten, ich mache Witze.
Zusammenfassung (Bezeichnung vom Original geändert, um die 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 das Sprachkonstrukt, das einen bedingungslosen Zweig verursacht. Alternativen hängen von der Verwendung von Strukturen ab, die bedingte Verzweigungen mit einem entarteten, immer zutreffenden Zustand unterstützen. - Das Etikett dokumentiert die Absicht ohne zusätzliche Kommentare.
- Der Leser muss den dazwischenliegenden Code nicht nach frühen
break
s scannen (obwohl es für einen Hacker ohne Prinzipien immer noch möglich ist,continue
mit einem frühen zu simulierengoto
).
Regeln:
- Stellen Sie sich vor, die Gotophoben hätten nicht gewonnen. Es versteht sich, dass das oben Genannte nicht in echtem Code verwendet werden kann, da es gegen etablierte Redewendungen verstößt.
- Nehmen wir an, wir haben alle von "Goto als schädlich angesehen" gehört und wissen, dass goto zum Schreiben von Spaghetti-Code verwendet werden kann.
- Wenn Sie mit einem Beispiel nicht einverstanden sind, kritisieren Sie es nur aus technischen Gründen ("Weil die Leute goto nicht mögen" ist kein technischer Grund).
Mal sehen, ob wir wie Erwachsene darüber sprechen können.
Bearbeiten
Diese Frage scheint jetzt erledigt zu sein. Es wurden einige qualitativ hochwertige Antworten generiert. Vielen Dank an alle, besonders an diejenigen, die mein kleines Loop-Beispiel ernst genommen haben. Die meisten Skeptiker waren besorgt über den fehlenden Blockumfang. Wie @quinmars in einem Kommentar hervorhob, können Sie den Loop-Body immer mit geschweiften Klammern versehen. Ich stelle dies nebenbei fest for(;;)
und while(true)
gebe Ihnen die Zahnspange auch nicht kostenlos ( und wenn Sie sie weglassen, kann dies zu ärgerlichen Fehlern führen). Wie auch immer, ich werde nicht mehr von Ihrer Gehirnleistung für diese Kleinigkeit verschwenden - ich kann mit den harmlosen und idiomatischen for(;;)
und while(true)
(genauso gut, wenn ich meinen Job behalten will) leben.
In Anbetracht der anderen Antworten sehe ich, dass viele Leute dies goto
als etwas ansehen , das Sie immer auf eine andere Weise umschreiben müssen. Natürlich können Sie dies vermeiden, goto
indem Sie eine Schleife, ein zusätzliches Flag, einen Stapel verschachtelter if
s oder was auch immer einführen , aber warum nicht überlegen, ob dies goto
vielleicht das 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 integrierte Sprachfunktion für den beabsichtigten Zweck verwendet wird? Meiner Meinung nach ist das Hinzufügen einer Flagge zu teuer, um sie zu bezahlen. Ich mag es, wenn meine Variablen Dinge in den Problem- oder Lösungsbereichen darstellen. "Nur um ein zu vermeiden goto
" schneidet es nicht.
Ich akzeptiere die erste Antwort, die das C-Muster für die Verzweigung zu einem Bereinigungsblock ergab. IMO, dies ist das stärkste Argument für eine goto
der veröffentlichten Antworten, sicherlich wenn Sie es an den Verzerrungen messen, die ein Hasser durchmachen muss, um es zu vermeiden.
for (;;)
) hat keine überraschenden Einstiegspunkte.Antworten:
Hier ist ein Trick, den ich von Leuten gehört habe, die ihn benutzen. Ich habe es aber noch nie in freier Wildbahn gesehen. Und es gilt nur für C, weil C ++ RAII hat, um dies idiomatischer 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; }
quelle
Das klassische Bedürfnis nach GOTO in C ist wie folgt
for ... for ... if(breakout_condition) goto final; final:
Es gibt keine einfache Möglichkeit, aus verschachtelten Schleifen ohne goto auszubrechen.
quelle
Hier ist mein nicht dummes Beispiel (von Stevens APITUE) für Unix-Systemaufrufe, die möglicherweise durch ein Signal unterbrochen werden.
restart: if (system_call() == -1) { if (errno == EINTR) goto restart; // handle real errors }
Die Alternative ist eine entartete Schleife. Diese Version liest sich wie Englisch "Wenn der Systemaufruf durch ein Signal unterbrochen wurde, starten Sie es neu".
quelle
continue
ist nurgoto
eine Maske.else break
s benötigt (eines für jedes ifs), um es gleichwertig zu machen .. was soll ich sagen ...goto
oder nicht, dein Code ist nur so gut wie du :(Wenn Duffs Gerät kein Goto braucht, sollten Sie es 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 .
quelle
Knuth hat einen Artikel "Strukturierte Programmierung mit GOTO-Anweisungen" geschrieben, den Sie zB hier erhalten können . Dort finden Sie viele Beispiele.
quelle
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 benutze,
goto
ist das Vorwärtsspringen, normalerweise aus Blöcken und niemals in Blöcke. Dies vermeidet den Missbrauchdo{}while(0)
und anderer Konstrukte, die die Verschachtelung erhöhen, während lesbarer, strukturierter Code erhalten bleibt.quelle
do_stuff(thingy) { RAIILockerObject(thingy); ..... /* -->*/ } /* <---*/
:)try { lock();..code.. } finally { unlock(); }
aber ja, C hat kein Äquivalent.Ich habe nichts gegen gotos im Allgemeinen, aber ich kann mir mehrere Gründe vorstellen, warum Sie sie nicht für eine Schleife verwenden möchten, wie Sie es erwähnt haben:
quelle
Ein guter Ort, um ein goto zu verwenden, ist ein Vorgang, der an mehreren Punkten abgebrochen werden kann, von denen jeder verschiedene Bereinigungsstufen erfordert. Gotophobe können die Gotos immer durch strukturierten Code und eine Reihe von Tests ersetzen, aber ich denke, dies ist einfacher, da übermäßige Einrückungen vermieden werden:
quelle
freeResourcesCloseFileQuit
,closeFileQuit
, undquit
.@ fizzer.myopenid.com: Ihr geposteter Code-Ausschnitt entspricht dem Folgenden:
while (system_call() == -1) { if (errno != EINTR) { // handle real errors break; } }
Ich bevorzuge definitiv diese Form.
quelle
Obwohl ich dieses Muster im Laufe der Zeit immer mehr hasse, ist es in die COM-Programmierung integriert.
#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; }
quelle
Hier ist ein Beispiel für ein gutes Goto:
// No Code
quelle
Ich habe gesehen, wie ich richtig eingesetzt wurde, aber die Situationen sind normalerweise hässlich. Es ist nur, wenn der Gebrauch von sich
goto
selbst so viel weniger schlecht ist als das Original. @ Johnathon Holland das Problem ist, 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 es
while(true)
.for (;;) { //code goes here }
quelle
== true
entfernt. Das kommt meinem wirklichen Stil sowieso näher. :)Mein Problem dabei ist, dass Sie das Block Scoping verlieren. Alle zwischen den gotos deklarierten lokalen Variablen bleiben in Kraft, falls die Schleife jemals unterbrochen wird. (Vielleicht nehmen Sie an, dass die Schleife für immer läuft. Ich glaube jedoch nicht, dass der ursprüngliche Fragesteller dies gestellt hat.)
Das Problem des Scoping ist bei C ++ eher ein Problem, da einige Objekte möglicherweise davon abhängen, ob ihr dtor zu geeigneten Zeiten aufgerufen wird.
Für mich ist der beste Grund, goto zu verwenden, ein mehrstufiger Initialisierungsprozess, bei dem es wichtig ist, dass alle Inits zurückgesetzt werden, wenn einer fehlschlägt.
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;
quelle
Ich benutze goto's selbst nicht, aber ich habe einmal mit einer Person gearbeitet, die sie in bestimmten Fällen benutzt hat. Wenn ich mich richtig erinnere, lag sein Grundprinzip bei Leistungsproblemen - er hatte auch spezifische Regeln dafür, wie . Immer in der gleichen Funktion, und das Label war immer UNTER der goto-Anweisung.
quelle
#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); }
quelle
@ Greg:
Warum machen Sie Ihr Beispiel nicht so:
void foo() { if (doA()) { if (doB()) { if (!doC()) { UndoA(); UndoB(); } } else { UndoA(); } } return; }
quelle