Es gibt einige Gründe für die Verwendung der mir bekannten "goto" -Anweisung (einige haben bereits darüber gesprochen):
Funktion sauber verlassen
In einer Funktion können Sie häufig Ressourcen zuweisen und müssen an mehreren Stellen beendet werden. Programmierer können ihren Code vereinfachen, indem sie den Ressourcenbereinigungscode am Ende der Funktion einfügen, und alle "Austrittspunkte" der Funktion werden auf das Bereinigungsetikett gesetzt. Auf diese Weise müssen Sie nicht an jedem "Exit-Punkt" der Funktion Bereinigungscode schreiben.
Verschachtelte Schleifen verlassen
Wenn Sie sich in einer verschachtelten Schleife befinden und aus allen Schleifen ausbrechen müssen , kann ein goto dies viel sauberer und einfacher machen als break-Anweisungen und if-Checks.
Leistungsverbesserungen auf niedriger Ebene
Dies gilt nur für perf-kritischen Code, aber goto-Anweisungen werden sehr schnell ausgeführt und können Ihnen beim Durchlaufen einer Funktion einen Schub geben. Dies ist jedoch ein zweischneidiges Schwert, da ein Compiler normalerweise keinen Code optimieren kann, der gotos enthält.
Beachten Sie, dass in all diesen Beispielen gotos auf den Umfang einer einzelnen Funktion beschränkt sind.
goto
durchreturn
ist einfach albern. Es wird nichts "umgestaltet", sondern nur "umbenannt", damit Menschen, die in einergoto
unterdrückten Umgebung aufgewachsen sind (dh wir alle), sich besser fühlen, wenn sie das verwenden, was moralisch gleichbedeutend ist mit agoto
. Ich sehe die Schleife lieber dort, wo ich sie benutze, und sehe ein weniggoto
, was für sich genommen nur ein Werkzeug ist , als jemanden zu sehen, der die Schleife an einen Ort verschoben hat, der nichts damit zu tun hat, nur um a zu vermeidengoto
.break
,continue
,return
ist im Grundegoto
nur in schöner Verpackung.do{....}while(0)
eine bessere Idee als goto sein soll, außer dass es in Java funktioniert.Jeder, der
goto
direkt oder indirekt gegen Edsger Dijkstras Artikel GoTo Considered Harmful spricht , begründet seine Position. Schade, dass Dijkstras Artikel praktisch nichts mit der Artgoto
und Weise zu tun hat, wie Aussagen heutzutage verwendet werden, und daher ist das, was der Artikel sagt, für die moderne Programmierszene kaum oder gar nicht anwendbar. Dasgoto
-lose Mem grenzt jetzt an eine Religion, bis hin zu seinen Schriften, die von oben diktiert wurden, seinen Hohepriestern und dem Meiden (oder Schlimmeren) von wahrgenommenen Ketzern.Lassen Sie uns Dijkstras Artikel in einen Kontext stellen, um ein wenig Licht in das Thema zu bringen.
Als Dijkstra seine Arbeit schrieb, waren die populären Sprachen der Zeit unstrukturierte prozedurale Sprachen wie BASIC, FORTRAN (die früheren Dialekte) und verschiedene Assemblersprachen. Es war durchaus üblich, dass Leute, die die höheren Sprachen verwendeten, in verdrehten, verzerrten Ausführungsthreads, die den Begriff "Spaghetti-Code" hervorbrachten, über ihre gesamte Codebasis sprangen. Sie können dies sehen, indem Sie zu dem klassischen Trek-Spiel von Mike Mayfield übergehen und versuchen, herauszufinden, wie die Dinge funktionieren. Nehmen Sie sich einen Moment Zeit, um das zu überprüfen.
Dies ist "die ungezügelte Verwendung der Go-to-Statement", gegen die Dijkstra 1968 in seiner Zeitung schimpfte . Dies ist die Umgebung, in der er lebte, die ihn dazu veranlasste, diese Zeitung zu schreiben. Die Fähigkeit, zu jedem beliebigen Zeitpunkt in Ihrem Code zu springen, war das, was er kritisierte und forderte, gestoppt zu werden. Ein Vergleich mit den anämischen Kräften
goto
in C oder anderen moderneren Sprachen ist einfach riskant.Ich kann bereits die erhobenen Gesänge der Kultisten hören, wenn sie sich dem Ketzer stellen. "Aber", werden sie singen, "Sie können es sehr schwierig machen, Code
goto
in C zu lesen ." Oh ja? Sie können auch das Lesen von Code sehr schwierig machengoto
. Wie dieser:Nicht
goto
in Sicht, also muss es leicht zu lesen sein, oder? Oder wie wäre es mit diesem:Nein auch
goto
dort. Es muss daher lesbar sein.Was ist mein Punkt mit diesen Beispielen? Es sind keine Sprachfunktionen, die unlesbaren, nicht wartbaren Code machen. Es ist nicht die Syntax, die das macht. Es sind schlechte Programmierer, die das verursachen. Und schlechte Programmierer können, wie Sie in diesem Punkt sehen können, jede Sprachfunktion unlesbar und unbrauchbar machen. Wie die
for
Schleifen dort oben. (Sie können sie sehen, richtig?)Um fair zu sein, sind einige Sprachkonstrukte leichter zu missbrauchen als andere. Wenn Sie jedoch ein C-Programmierer sind, würde ich ungefähr 50% der Verwendungszwecke viel genauer betrachten,
#define
lange bevor ich einen Kreuzzug gegen sie führen würdegoto
!Für diejenigen, die sich die Mühe gemacht haben, bis hierher zu lesen, gibt es einige wichtige Punkte zu beachten.
goto
Aussagen wurde für eine Programmierumgebung geschrieben , in demgoto
wurde eine Menge mehr potenziell schädlich , als es in den meisten modernen Sprachen, die nicht ein Assembler sind.goto
ist ungefähr so rational wie das Sprichwort "Ich habe einmal versucht, Spaß zu haben, aber es hat mir nicht gefallen, also bin ich jetzt dagegen".goto
Anweisungen im Code, die nicht angemessen durch andere Konstrukte ersetzt werden können.godo
" Greuel, bei dem eine immer falschedo
Schleifebreak
anstelle von a aus der Verwendung herausgebrochen wirdgoto
. Diese sind oft schlimmer als vernünftiger Gebrauchgoto
.quelle
goto
tatsächlich sind (was die gestellte Frage ist)Das blinde Befolgen von Best Practices ist keine Best Practice. Die Idee,
goto
Aussagen als primäre Form der Flusskontrolle zu vermeiden, besteht darin, zu vermeiden, dass unlesbarer Spaghetti-Code erzeugt wird. Wenn sie an den richtigen Stellen sparsam eingesetzt werden, können sie manchmal die einfachste und klarste Art sein, eine Idee auszudrücken. Walter Bright, der Erfinder des Zortech C ++ - Compilers und der Programmiersprache D, verwendet sie häufig, aber mit Bedacht. Trotz dergoto
Aussagen ist sein Code immer noch perfekt lesbar.Fazit: Vermeiden
goto
um zu vermeidengoto
ist sinnlos. Was Sie wirklich vermeiden möchten, ist das Erstellen von unlesbarem Code. Wenn Ihrgoto
geladener Code lesbar ist, ist daran nichts auszusetzen.quelle
Da
goto
es schwierig ist, über den Programmfluss nachzudenken 1 (auch bekannt als „Spaghetti-Code“),goto
wird dies im Allgemeinen nur verwendet, um fehlende Funktionen zu kompensieren: Die Verwendung vongoto
kann tatsächlich akzeptabel sein, aber nur, wenn die Sprache keine strukturiertere Variante bietet das gleiche Ziel. Nehmen Sie das Beispiel von Doubt:Dies ist wahr - aber nur, wenn die Sprache keine strukturierte Ausnahmebehandlung mit Bereinigungscode (wie RAII oder
finally
) zulässt , die den gleichen Job besser erledigt (da sie speziell dafür entwickelt wurde), oder wenn es einen guten Grund dafür gibt strukturierte Ausnahmebehandlung anwenden (aber Sie werden diesen Fall nie haben, außer auf einer sehr niedrigen Ebene).In den meisten anderen Sprachen besteht die einzig akzeptable Verwendung
goto
darin, verschachtelte Schleifen zu beenden. Und selbst dort ist es fast immer besser, die äußere Schleife in eine eigene Methode zu heben undreturn
stattdessen zu verwenden.Davon abgesehen
goto
ist dies ein Zeichen dafür, dass nicht genügend Gedanken in den jeweiligen Code eingeflossen sind.1 Moderne Sprachen, die die
goto
Implementierung einiger Einschränkungen unterstützen (z. B.goto
nicht in Funktionen springen oder aus Funktionen herausspringen), aber das Problem bleibt im Wesentlichen dasselbe.Dies gilt übrigens natürlich auch für andere Sprachmerkmale, insbesondere für Ausnahmen. In der Regel gelten strenge Regeln, um diese Funktionen nur dort zu verwenden, wo dies angegeben ist, z. B. die Regel, keine Ausnahmen zur Steuerung des nicht außergewöhnlichen Programmflusses zu verwenden.
quelle
finally
? Die Verwendung von Ausnahmen für andere Dinge als die Fehlerbehandlung ist also gut, aber die Verwendunggoto
ist schlecht? Ich denke, Ausnahmen sind ziemlich treffend benannt.Nun, es gibt eine Sache, die immer schlimmer ist als
goto's
; seltsame Verwendung anderer Programmablaufoperatoren, um ein Goto zu vermeiden:Beispiele:
quelle
do{}while(false)
Ich denke, kann als idiomatisch angesehen werden. Sie dürfen nicht widersprechen: Dgoto after_do_block;
ohne das tatsächlich zu sagen. Sonst ... eine "Schleife", die genau einmal läuft? Ich würde das Missbrauch von Kontrollstrukturen nennen.#define
viele, viele Male viel schlimmer sind als ab und zu zu verwendengoto
: DIn C # Schalter Aussage erlauben doest nicht durchsturz . Daher wird goto verwendet, um die Steuerung auf ein bestimmtes Switch-Case-Label oder das Standard- Label zu übertragen.
Beispielsweise:
Bearbeiten: Es gibt eine Ausnahme bei der Regel "Kein Fall-Through". Fall-through ist zulässig, wenn eine case-Anweisung keinen Code enthält.
quelle
goto case 5:
wann Sie in Fall 1 sind). Es scheint, als ob Konrad Rudolphs Antwort hier richtig ist: Siegoto
kompensiert ein fehlendes Merkmal (und ist weniger klar als das eigentliche Merkmal). Wenn wir wirklich einen Durchfall wollen, wäre der vielleicht beste Standard kein Durchfall, sonderncontinue
eine explizite Anforderung.#ifdef TONGUE_IN_CHEEK
Perl hat eine
goto
, mit der Sie die Tail Calls von Armen implementieren können. :-P#endif
Okay, das hat also nichts mit Cs zu tun
goto
. Im Ernst, ich stimme den anderen Kommentaren zur Verwendunggoto
für Bereinigungen oder zur Implementierung von Duffs Gerät oder dergleichen zu. Es geht nur darum zu benutzen, nicht zu missbrauchen.(Der gleiche Kommentar kann für
longjmp
Ausnahmencall/cc
und dergleichen gelten - sie haben legitime Verwendungszwecke, können jedoch leicht missbraucht werden. Beispielsweise kann eine Ausnahme nur ausgelöst werden, um einer tief verschachtelten Kontrollstruktur unter völlig nicht außergewöhnlichen Umständen zu entgehen .)quelle
Ich habe im Laufe der Jahre mehr als ein paar Zeilen Assemblersprache geschrieben. Letztendlich kompiliert sich jede Hochsprache zu gotos. Okay, nenne sie "Zweige" oder "Sprünge" oder was auch immer, aber sie sind gotos. Kann jemand einen Assembler ohne Goto schreiben?
Jetzt können Sie sicher einen Fortran-, C- oder BASIC-Programmierer darauf hinweisen, dass es ein Rezept für Spaghetti Bolognese ist, mit Gotos aufzuregen. Die Antwort ist jedoch nicht, sie zu vermeiden, sondern sie vorsichtig zu verwenden.
Ein Messer kann verwendet werden, um Essen zuzubereiten, jemanden zu befreien oder jemanden zu töten. Verzichten wir aus Angst vor letzteren auf Messer? Ebenso das goto: unachtsam eingesetzt behindert es, vorsichtig eingesetzt hilft es.
quelle
Werfen Sie einen Blick auf die Verwendung von Springen beim Programmieren in C :
Was meine ich? Möglicherweise haben Sie einen Code, der folgendermaßen aussieht:
Dies ist in Ordnung, bis Sie feststellen, dass Sie Ihren Bereinigungscode ändern müssen. Dann müssen Sie 4 Änderungen vornehmen. Jetzt können Sie entscheiden, dass Sie die gesamte Bereinigung in einer einzigen Funktion zusammenfassen können. Das ist keine schlechte Idee. Dies bedeutet jedoch, dass Sie mit Zeigern vorsichtig sein müssen. Wenn Sie einen Zeiger in Ihrer Bereinigungsfunktion freigeben möchten, können Sie ihn nicht so einstellen, dass er auf NULL zeigt, es sei denn, Sie übergeben einen Zeiger auf einen Zeiger. In vielen Fällen werden Sie diesen Zeiger sowieso nicht mehr verwenden, sodass dies möglicherweise kein großes Problem darstellt. Wenn Sie andererseits einen neuen Zeiger, ein neues Dateihandle oder ein anderes Element hinzufügen, das bereinigt werden muss, müssen Sie Ihre Bereinigungsfunktion erneut ändern. und dann müssen Sie die Argumente für diese Funktion ändern.
Mit
goto
wird es seinDer Vorteil hierbei ist, dass Ihr Code nach dem Ende Zugriff auf alles hat, was für die Bereinigung erforderlich ist, und Sie es geschafft haben, die Anzahl der Änderungspunkte erheblich zu reduzieren. Ein weiterer Vorteil ist, dass Sie nicht mehr mehrere Ausstiegspunkte für Ihre Funktion haben, sondern nur noch einen. Es besteht keine Möglichkeit, dass Sie versehentlich von der Funktion zurückkehren, ohne aufzuräumen.
Da goto nur zum Springen zu einem einzelnen Punkt verwendet wird, ist es nicht so, als würden Sie eine Menge Spaghetti-Code erstellen, der hin und her springt, um Funktionsaufrufe zu simulieren. Vielmehr hilft goto tatsächlich dabei, strukturierteren Code zu schreiben.
Mit einem Wort,
goto
sollte immer sparsam und als letztes Mittel eingesetzt werden - aber es gibt eine Zeit und einen Ort dafür. Die Frage sollte nicht "Müssen Sie es verwenden" sein, sondern "Ist es die beste Wahl", um es zu verwenden.quelle
Einer der Gründe, warum goto schlecht ist, ist neben dem Codierungsstil, dass Sie damit überlappende , aber nicht verschachtelte Schleifen erstellen können :
Dies würde die bizarre, aber möglicherweise legale Kontrollflussstruktur erzeugen, in der eine Sequenz wie (a, b, c, b, a, b, a, b, ...) möglich ist, was Compiler-Hacker unglücklich macht. Anscheinend gibt es eine Reihe cleverer Optimierungstricks, die darauf beruhen, dass diese Art von Struktur nicht auftritt. (Ich sollte meine Kopie des Drachenbuchs überprüfen ...) Dies könnte (unter Verwendung einiger Compiler) dazu führen, dass andere Optimierungen für Code, der
goto
s enthält, nicht durchgeführt werden .Es kann nützlich sein, wenn Sie wissen , dass "oh, übrigens" den Compiler davon überzeugt, schnelleren Code auszugeben. Persönlich würde ich lieber versuchen, dem Compiler zu erklären, was wahrscheinlich ist und was nicht, bevor ich einen Trick wie goto verwende, aber ich könnte es auch versuchen,
goto
bevor ich den Assembler hacke.quelle
goto
die nützlich sind, ist, dass Sie damit Schleifen wie diese erstellen können, die ansonsten eine Reihe logischer Verzerrungen erfordern würden. Ich würde weiter argumentieren, dass wenn der Optimierer nicht weiß, wie man das umschreibt, dann gut . Eine solche Schleife sollte nicht aus Gründen der Leistung oder Lesbarkeit durchgeführt werden, sondern weil dies genau die Reihenfolge ist, in der die Dinge geschehen müssen. In diesem Fall möchte ich nicht, dass der Optimierer damit herumschraubt.Ich finde es lustig, dass einige Leute so weit gehen, eine Liste von Fällen zu erstellen, in denen goto akzeptabel ist, und sagen, dass alle anderen Verwendungen nicht akzeptabel sind. Denken Sie wirklich, dass Sie jeden Fall kennen, in dem goto die beste Wahl ist, um einen Algorithmus auszudrücken?
Zur Veranschaulichung gebe ich Ihnen ein Beispiel, das hier noch niemand gezeigt hat:
Heute habe ich Code zum Einfügen eines Elements in eine Hash-Tabelle geschrieben. Die Hash-Tabelle ist ein Cache früherer Berechnungen, der nach Belieben überschrieben werden kann (was sich auf die Leistung, aber nicht auf die Richtigkeit auswirkt).
Jeder Bucket der Hash-Tabelle hat 4 Slots, und ich habe eine Reihe von Kriterien, um zu entscheiden, welches Element überschrieben werden soll, wenn ein Bucket voll ist. Im Moment bedeutet dies, bis zu drei Durchgänge durch einen Eimer zu machen, wie folgt:
Wenn ich goto nicht verwenden würde, wie würde dieser Code aussehen?
Etwas wie das:
Es würde immer schlechter aussehen, wenn mehr Durchgänge hinzugefügt würden, während die Version mit goto jederzeit die gleiche Einrückungsstufe beibehält und die Verwendung von falschen if-Anweisungen vermeidet, deren Ergebnis durch die Ausführung der vorherigen Schleife impliziert wird.
Es gibt also einen anderen Fall, in dem goto den Code sauberer und einfacher zu schreiben und zu verstehen macht ... Ich bin sicher, es gibt noch viel mehr. Geben Sie also nicht vor, alle Fälle zu kennen, in denen goto nützlich ist, und lehnen Sie alle guten ab, die Sie nicht könnten Ich denke nicht daran.
quelle
goto
dass jede Funktion auf derselben Abstraktionsebene liegt. Dass es vermeidet,goto
ist ein Bonus.container::iterator it = slot_p.find(hash_key); if (it != slot_p.end()) it->overwrite(hash_key); else it = slot_p.find_first_empty();
Ich finde diese Art der Programmierung viel einfacher zu lesen. Jede Funktion könnte in diesem Fall als reine Funktion geschrieben werden, über die viel einfacher nachzudenken ist. Die Hauptfunktion erklärt nun anhand des Namens der Funktionen, was der Code tut. Wenn Sie möchten, können Sie anhand der Definitionen herausfinden, wie er funktioniert.Die Regel bei goto, die wir verwenden, lautet, dass goto in Ordnung ist, um vorwärts zu einem einzelnen Exit-Bereinigungspunkt in einer Funktion zu springen. In wirklich komplexen Funktionen lockern wir diese Regel, um andere Vorwärtssprünge zu ermöglichen. In beiden Fällen vermeiden wir tief verschachtelte Anweisungen, die häufig bei der Fehlercodeprüfung auftreten, was die Lesbarkeit und Wartung verbessert.
quelle
Die nachdenklichste und gründlichste Diskussion über goto-Anweisungen, ihre legitimen Verwendungen und alternativen Konstrukte, die anstelle von "tugendhaften goto-Anweisungen" verwendet werden können, aber genauso leicht missbraucht werden können wie goto-Anweisungen, ist Donald Knuths Artikel " Strukturierte Programmierung mit goto-Anweisungen ". , in den Computing Surveys vom Dezember 1974 (Band 6, Nr. 4. S. 261 - 301).
Es überrascht nicht, dass einige Aspekte dieses 39 Jahre alten Papiers datiert sind: Durch die Erhöhung der Verarbeitungsleistung um Größenordnungen werden einige der Leistungsverbesserungen von Knuth bei Problemen mittlerer Größe nicht bemerkt, und seitdem wurden neue Konstrukte für Programmiersprachen erfunden. (Zum Beispiel fassen Try-Catch-Blöcke Zahns Konstrukt zusammen, obwohl sie selten auf diese Weise verwendet werden.) Knuth deckt jedoch alle Seiten des Arguments ab und sollte gelesen werden müssen, bevor jemand das Problem erneut aufgreift.
quelle
In einem Perl-Modul möchten Sie gelegentlich Unterprogramme oder Abschlüsse im laufenden Betrieb erstellen. Die Sache ist, dass, sobald Sie die Unterroutine erstellt haben, wie Sie dazu kommen. Sie könnten es einfach aufrufen, aber wenn das Unterprogramm es verwendet, ist
caller()
es nicht so hilfreich wie es sein könnte. Hier kann diegoto &subroutine
Variation hilfreich sein.Hier ist ein kurzes Beispiel:
Sie können diese Form auch verwenden
goto
, um eine rudimentäre Form der Tail-Call-Optimierung bereitzustellen.(In Perl 5 Version 16 wäre das besser geschrieben als
goto __SUB__;
)Es gibt ein Modul, das einen
tail
Modifikator importiert, und eines, das importiert wird,recur
wenn Sie diese Form von nicht mögengoto
.Die meisten anderen Gründe für die Verwendung lassen
goto
sich besser mit anderen Schlüsselwörtern erledigen.Wie
redo
ein bisschen Code:Oder gehen Sie
last
von mehreren Stellen zum Code?quelle
C hat keine mehrstufige / beschriftete Unterbrechung, und nicht alle Kontrollflüsse können einfach mit den Iterations- und Entscheidungsprimitiven von C modelliert werden. gotos tragen wesentlich dazu bei, diese Mängel zu beheben.
Manchmal ist es klarer, eine Flag-Variable zu verwenden, um eine Art Pseudo-Multi-Level-Break zu bewirken, aber sie ist dem goto nicht immer überlegen (zumindest kann man mit goto im Gegensatz zu einer Flag-Variablen leicht bestimmen, wohin die Steuerung geht ), und manchmal möchten Sie einfach nicht den Leistungspreis von Flaggen / anderen Verrenkungen bezahlen, um das Goto zu vermeiden.
libavcodec ist ein leistungsempfindlicher Code. Der direkte Ausdruck des Kontrollflusses hat wahrscheinlich Priorität, da er tendenziell besser läuft.
quelle
Genauso gut hat niemand jemals die Anweisung "COME FROM" implementiert ....
quelle
Ich finde das do {} während (falscher) Gebrauch absolut abstoßend. Es ist denkbar, mich davon zu überzeugen, dass es in einem seltsamen Fall notwendig ist, aber niemals, dass es sich um sauberen, vernünftigen Code handelt.
Wenn Sie eine solche Schleife ausführen müssen, warum nicht die Abhängigkeit von der Flag-Variablen explizit machen?
quelle
/*empty*/
seinstepfailed = 1
? Wie ist das auf jeden Fall besser als eindo{}while(0)
? In beiden Fällen müssen Siebreak
sich davon lösen (oder von Ihnenstepfailed = 1; continue;
). Scheint mir unnötig.1) Die häufigste Verwendung von goto, die ich kenne, ist die Emulation der Ausnahmebehandlung in Sprachen, die sie nicht anbieten, nämlich in C. (Der von Nuclear oben angegebene Code ist genau das.) Schauen Sie sich den Linux-Quellcode an und Sie ' Ich werde eine Unmenge von Gotos sehen, die auf diese Weise verwendet werden. Laut einer 2013 durchgeführten Kurzumfrage gab es im Linux-Code etwa 100.000 Gotos: http://blog.regehr.org/archives/894 . Die Verwendung von Goto wird sogar im Linux-Codierungsstil-Handbuch erwähnt: https://www.kernel.org/doc/Documentation/CodingStyle . So wie die objektorientierte Programmierung mit Strukturen emuliert wird, die mit Funktionszeigern gefüllt sind, hat goto seinen Platz in der C-Programmierung. Wer hat Recht: Dijkstra oder Linus (und alle Linux-Kernel-Codierer)? Im Grunde ist es Theorie gegen Praxis.
Es gibt jedoch das übliche Problem, keine Unterstützung auf Compilerebene zu haben und nach gängigen Konstrukten / Mustern zu suchen: Es ist einfacher, sie falsch zu verwenden und Fehler ohne Überprüfungen zur Kompilierungszeit einzuführen. Windows und Visual C ++, jedoch im C-Modus, bieten aus genau diesem Grund eine Ausnahmebehandlung über SEH / VEH: Ausnahmen sind auch außerhalb von OOP-Sprachen nützlich, dh in einer prozeduralen Sprache. Der Compiler kann Ihren Speck jedoch nicht immer speichern, selbst wenn er syntaktische Unterstützung für Ausnahmen in der Sprache bietet. Betrachten Sie als Beispiel für den letzteren Fall den berühmten Apple SSL-Fehler "goto fail", der nur einen goto mit katastrophalen Folgen duplizierte ( https://www.imperialviolet.org/2014/02/22/applebug.html ):
Mit vom Compiler unterstützten Ausnahmen können Sie genau denselben Fehler haben, z. B. in C ++:
Beide Varianten des Fehlers können jedoch vermieden werden, wenn der Compiler Sie analysiert und Sie vor nicht erreichbarem Code warnt. Wenn Sie beispielsweise mit Visual C ++ auf der Warnstufe / W4 kompilieren, wird der Fehler in beiden Fällen gefunden. Java zum Beispiel verbietet nicht erreichbaren Code (wo er gefunden werden kann!) Aus einem ziemlich guten Grund: Es ist wahrscheinlich ein Fehler im durchschnittlichen Joe-Code. Solange das goto-Konstrukt keine Ziele zulässt, die der Compiler nicht leicht herausfinden kann, wie gotos zu berechneten Adressen (**), ist es für den Compiler nicht schwieriger, nicht erreichbaren Code in einer Funktion mit gotos zu finden, als Dijkstra zu verwenden -genehmigter Code.
(**) Fußnote: In einigen Versionen von Basic sind Gotos zu berechneten Zeilennummern möglich, z. B. GOTO 10 * x, wobei x eine Variable ist. Eher verwirrend bezieht sich in Fortran "computed goto" auf ein Konstrukt, das einer switch-Anweisung in C entspricht. Standard C erlaubt keine berechneten gotos in der Sprache, sondern nur gotos für statisch / syntaktisch deklarierte Labels. GNU C hat jedoch eine Erweiterung, um die Adresse eines Labels abzurufen (der unäre Operator, das Präfix &&) und ermöglicht auch das Wechseln zu einer Variablen vom Typ void *. Weitere Informationen zu diesem obskuren Unterthema finden Sie unter https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html . Der Rest dieses Beitrags befasst sich nicht mit dieser obskuren GNU C-Funktion.
Standard-C-Gotos (dh nicht berechnete Gotos) sind normalerweise nicht der Grund, warum nicht erreichbarer Code zur Kompilierungszeit nicht gefunden werden kann. Der übliche Grund ist Logikcode wie der folgende. Gegeben
Für einen Compiler ist es genauso schwierig, nicht erreichbaren Code in einem der folgenden 3 Konstrukte zu finden:
(Entschuldigen Sie meinen Klammer-bezogenen Codierungsstil, aber ich habe versucht, die Beispiele so kompakt wie möglich zu halten.)
Visual C ++ / W4 (auch mit / Ox) kann in keinem dieser Bereiche nicht erreichbaren Code finden, und wie Sie wahrscheinlich wissen, ist das Problem, nicht erreichbaren Code zu finden, im Allgemeinen nicht zu entscheiden. (Wenn Sie mir das nicht glauben: https://www.cl.cam.ac.uk/teaching/2006/OptComp/slides/lecture02.pdf )
Als verwandtes Problem kann das C goto verwendet werden, um Ausnahmen nur innerhalb des Körpers einer Funktion zu emulieren. Die Standard-C-Bibliothek bietet ein Funktionspaar setjmp () und longjmp () zum Emulieren nicht lokaler Exits / Ausnahmen, die jedoch im Vergleich zu anderen Sprachen einige schwerwiegende Nachteile aufweisen. Der Wikipedia-Artikel http://en.wikipedia.org/wiki/Setjmp.h erklärt dieses letztere Problem ziemlich gut. Dieses Funktionspaar funktioniert auch unter Windows ( http://msdn.microsoft.com/en-us/library/yz2ez4as.aspx ), aber kaum jemand verwendet sie dort, weil SEH / VEH überlegen ist. Selbst unter Unix werden setjmp und longjmp meiner Meinung nach sehr selten verwendet.
2) Ich denke, die zweithäufigste Verwendung von goto in C ist die Implementierung einer mehrstufigen Unterbrechung oder einer mehrstufigen Fortsetzung, was ebenfalls ein ziemlich unumstrittener Anwendungsfall ist. Denken Sie daran, dass Java kein Goto-Label zulässt, aber Break-Label zulässt oder Label fortsetzt. Laut http://www.oracle.com/technetwork/java/simple-142616.html ist dies tatsächlich der häufigste Anwendungsfall von gotos in C (90% sagen sie), aber meiner subjektiven Erfahrung nach tendiert der Systemcode dazu um gotos häufiger für die Fehlerbehandlung zu verwenden. Vielleicht in wissenschaftlichem Code oder wenn das Betriebssystem Ausnahmebehandlung (Windows) bietet, sind mehrstufige Exits der dominierende Anwendungsfall. Sie geben keine genauen Angaben zum Kontext ihrer Umfrage.
Bearbeitet, um hinzuzufügen: Es stellt sich heraus, dass diese beiden Verwendungsmuster im C-Buch von Kernighan und Ritchie auf Seite 60 (je nach Ausgabe) zu finden sind. Eine andere bemerkenswerte Sache ist, dass beide Anwendungsfälle nur Vorwärts-Gotos beinhalten. Und es stellt sich heraus, dass die MISRA C 2012-Ausgabe (im Gegensatz zur 2004-Ausgabe) jetzt gotos erlaubt, solange es sich nur um Vorwärts-Gotos handelt.
quelle
Einige sagen, dass es in C ++ keinen Grund gibt, zu gehen. Einige sagen, dass es in 99% der Fälle bessere Alternativen gibt. Dies ist keine Argumentation, sondern nur irrationale Eindrücke. Hier ist ein solides Beispiel, in dem goto zu einem schönen Code führt, so etwas wie einer erweiterten Do-While-Schleife:
Vergleichen Sie es mit goto-freiem Code:
Ich sehe diese Unterschiede:
{}
Block wird benötigt (obwohldo {...} while
er vertrauter aussieht)loop
Variable benötigt, die an vier Stellen verwendet wirdloop
loop
enthält keine Daten, sondern steuert lediglich den Ablauf der Ausführung, der weniger verständlich ist als ein einfaches LabelEs gibt noch ein anderes Beispiel
Lassen Sie uns jetzt das "böse" loswerden:
Sie sehen, es ist die gleiche Art der Verwendung von goto, es ist ein gut strukturiertes Muster und es ist nicht vorwärts gehen, um so viele zu fördern, wie der einzig empfohlene Weg. Sicherlich möchten Sie "intelligenten" Code wie diesen vermeiden:
Der Punkt ist, dass goto leicht missbraucht werden kann, aber goto selbst ist nicht schuld. Beachten Sie, dass label in C ++ einen Funktionsumfang hat, sodass es den globalen Bereich nicht wie in einer reinen Assembly verschmutzt, in der überlappende Schleifen ihren Platz haben und sehr häufig sind - wie im folgenden Code für 8051, in dem die 7-Segment-Anzeige mit P1 verbunden ist. Das Programm schleift ein Blitzsegment um:
Es gibt noch einen weiteren Vorteil: goto kann als benannte Schleifen, Bedingungen und andere Flows dienen:
Oder Sie können ein gleichwertiges goto mit Einrückung verwenden, sodass Sie keinen Kommentar benötigen, wenn Sie den Markennamen mit Bedacht auswählen:
quelle
Verwenden Sie in Perl ein Label, um aus einer Schleife zu "gehen" - mit einer "last" -Anweisung, die break ähnelt.
Dies ermöglicht eine bessere Kontrolle über verschachtelte Schleifen.
Das traditionelle goto- Label wird ebenfalls unterstützt, aber ich bin mir nicht sicher, ob es zu viele Fälle gibt, in denen dies der einzige Weg ist, um das zu erreichen, was Sie wollen - Unterprogramme und Schleifen sollten in den meisten Fällen ausreichen.
quelle
goto &subroutine
. Womit das Unterprogramm mit dem aktuellen @_ gestartet wird, während das aktuelle Unterprogramm im Stapel ersetzt wird.Das Problem mit 'goto' und das wichtigste Argument der 'goto-less-Programmierung' ist, dass Ihr Code, wenn Sie ihn zu häufig verwenden, obwohl er sich möglicherweise richtig verhält, unlesbar, nicht wartbar, nicht überprüfbar usw. wird. In 99,99% von Die Fälle 'goto' führen zu Spaghetti-Code. Persönlich kann ich mir keinen guten Grund vorstellen, warum ich 'goto' verwenden würde.
quelle
goto
) abzuwägen . Die Verwendung von @ cschol ist ähnlich: Während er momentan vielleicht keine Sprache entwirft, bewertet er im Grunde die Bemühungen des Designers.goto
ist es billiger , eine Sprache zuzulassen, außer in Kontexten, in denen Variablen entstehen würden, als zu versuchen, jede Art von Kontrollstruktur zu unterstützen, die jemand benötigt. Das Schreiben von Code mit istgoto
möglicherweise nicht so gut wie das Verwenden einer anderen Struktur, aber wenn Sie solchen Code mit schreiben können,goto
vermeiden Sie "Lücken in der Ausdruckskraft" - Konstrukte, für die eine Sprache keinen effizienten Code schreiben kann.goto
auf dergoto
Codeüberprüfungsseite veröffentlicht, vereinfacht das Eliminieren des Codes die Logik des Codes erheblich.Das GOTO kann natürlich verwendet werden, aber es gibt eine wichtigere Sache als den Codestil oder ob der Code lesbar ist oder nicht, die Sie bei der Verwendung berücksichtigen müssen: Der darin enthaltene Code ist möglicherweise nicht so robust wie Sie denke nach .
Schauen Sie sich zum Beispiel die folgenden zwei Codefragmente an:
Ein äquivalenter Code mit GOTO
Das erste, was wir denken, ist, dass das Ergebnis beider Codebits der "Wert von A: 0" ist (wir nehmen natürlich eine Ausführung ohne Parallelität an).
Das ist nicht richtig: In der ersten Stichprobe ist A immer 0, in der zweiten Stichprobe (mit der GOTO-Anweisung) ist A möglicherweise nicht 0. Warum?
Der Grund ist, dass ich von einem anderen Punkt des Programms aus ein einfügen kann,
GOTO FINAL
ohne den Wert von A zu steuern.Dieses Beispiel ist sehr offensichtlich, aber wenn Programme komplizierter werden, steigt die Schwierigkeit, solche Dinge zu sehen.
Zugehöriges Material findet sich in dem berühmten Artikel von Herrn Dijkstra "Ein Fall gegen die GO TO-Erklärung".
quelle
Ich verwende goto im folgenden Fall: Wenn es erforderlich ist, von Funktionen an verschiedenen Orten zurückzukehren, und bevor es zurückkehrt, muss eine Uninitialisierung durchgeführt werden:
Nicht-Goto-Version:
gehe zur Version:
Die zweite Version macht es einfacher, wenn Sie etwas an den Freigabeanweisungen ändern müssen (jede wird einmal im Code verwendet), und verringert die Wahrscheinlichkeit, eine dieser Anweisungen zu überspringen, wenn Sie einen neuen Zweig hinzufügen. Das Verschieben in eine Funktion hilft hier nicht weiter, da die Freigabe auf verschiedenen "Ebenen" erfolgen kann.
quelle
finally
Blöcke in C #finally
). Verwenden Sie alternativgoto
s, aber zu einem gemeinsamen Austrittspunkt, der immer die gesamte Bereinigung durchführt. Jede Bereinigungsmethode kann jedoch entweder einen Wert verarbeiten, der null ist oder bereits bereinigt ist, oder sie wird durch einen bedingten Test geschützt, der übersprungen wird, wenn dies nicht angemessen ist.goto
s verwendet, die alle zum gleichen Austrittspunkt gehen, der die gleiche Logik hat (was, wie Sie sagen, ein zusätzliches if pro Ressource erfordert). AberC
egal , wenn Sie Recht haben - was auch immer der Grund ist, warum der Code in C ist, es ist mit ziemlicher Sicherheit ein Kompromiss, der den "direktesten" Code bevorzugt. (Mein Vorschlag behandelt komplexe Situationen, in denen eine bestimmte Ressource möglicherweise zugewiesen wurde oder nicht. Aber ja, in diesem Fall übertrieben.)Edsger Dijkstra, ein Informatiker, der wichtige Beiträge auf diesem Gebiet leistete, war auch dafür bekannt, die Verwendung von GoTo zu kritisieren. Es gibt einen kurzen Artikel über seine Argumentation auf Wikipedia .
quelle
Es ist praktisch für die zeichenweise Zeichenfolgenverarbeitung von Zeit zu Zeit.
Stellen Sie sich so etwas wie dieses printf-artige Beispiel vor:
Sie könnten dies
goto handle_literal
in einen Funktionsaufruf umgestalten , aber wenn mehrere verschiedene lokale Variablen geändert werden, müssten Sie Verweise auf jede Variable übergeben, es sei denn, Ihre Sprache unterstützt veränderbare Abschlüsse. Siecontinue
müssten nach dem Aufruf immer noch eine Anweisung verwenden (was wohl eine Form von goto ist), um dieselbe Semantik zu erhalten, wenn Ihre Logik dafür sorgt, dass ein anderer Fall nicht funktioniert.Ich habe gotos auch in Lexern mit Bedacht eingesetzt, normalerweise für ähnliche Fälle. Sie brauchen sie die meiste Zeit nicht, aber sie sind schön für diese seltsamen Fälle.
quelle