Lohnt es sich jemals, goto zu verwenden?

29

gotowird fast allgemein abgeraten. Lohnt es sich jemals, diese Aussage zu verwenden?

Casebash
quelle
16
xkcd.com/292
TheLQ
1
gotoist das, was die CPU letztendlich macht, aber es funktioniert nicht gut mit dem, was Menschen verstehen und abstrahieren müssen, weil es keine Markierung am Zielende gibt. Vergleiche mit Intercal COMEFROM.
2
@ ThorbjørnRavnAndersen, was bedeuten Menschen sonst noch, wenn sie von Zustandsübergängen in einer Zustandsmaschine sprechen ? Kannst du keine Ähnlichkeit sehen? "Geh in einen bestimmten Zustand ", das ist es, was es bedeutet und alles andere wäre nichts anderes als eine unnötige Komplikation einer so trivialen Sache. Und was meinst du mit "no mark"? Label ist eine solche Marke.
SK-logic
@ SK-Logik, hängt davon ab, wie weit Sie es zulassen, dass Goto kommt. Zustandsautomaten müssen nicht implementiert werden.
@ ThorbjørnRavnAndersen ist natürlich gotonicht erforderlich. Es ist nur die rationalste Art, sie in den imperativen Sprachen umzusetzen, da es der Semantik des Staatsübergangs am nächsten kommt. Und sein Ziel kann ziemlich weit von der Quelle entfernt sein, es wird die Lesbarkeit nicht behindern. Mein Lieblingsbeispiel für einen solchen Code ist DE Knuths Implementierung des Abenteuerspiels.
SK-logic

Antworten:

49

Dies wurde im Zusammenhang mit Stack Overflow mehrmals diskutiert, und Chris Gillum fasste die möglichen Verwendungen vongoto :

Funktion sauber verlassen

In einer Funktion können Sie häufig Ressourcen zuweisen und müssen an mehreren Stellen beenden. Programmierer können ihren Code vereinfachen, indem sie den Code für die Ressourcenbereinigung an das Ende der Funktion setzen. Alle "Exit Points" der Funktion würden auf das Bereinigungsetikett gesetzt. Auf diese Weise müssen Sie nicht an jedem "Exit-Punkt" der Funktion Bereinigungscode schreiben.

Verschachtelte Schleifen beenden

Wenn Sie sich in einer verschachtelten Schleife befinden und aus allen Schleifen ausbrechen müssen , kann ein goto dies viel übersichtlicher und einfacher machen als break-Anweisungen und if-Prüfungen.

Leistungsverbesserungen auf niedriger Ebene

Dies ist nur in perf-kritischem Code gültig, 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.

Ich würde argumentieren, wie viele andere argumentieren würden, dass in all diesen Fällen die Verwendung von gotoals Mittel verwendet wird, um aus einer Ecke herauszukommen, in die man sich selbst codiert hat, und im Allgemeinen ein Symptom für Code ist, der umgestaltet werden könnte.

Gemeinschaft
quelle
28
Das erste Problem wird sehr gut durch finallyBlöcke in modernen Sprachen gelöst, und das zweite Problem wird durch mit breaks bezeichnete gelöst . Wenn Sie jedoch mit C nicht weiterkommen, gotoist dies so ziemlich die einzige Möglichkeit, diese Probleme auf elegante Weise zu lösen.
Chinmay Kanchi
2
Sehr gute Punkte. Ich mag, wie Java erlaubt, aus mehreren Ebenen von Schleifen
auszubrechen
4
@Chinmay - finally-Blöcke gelten nur für 1) moderne Sprachen, in denen sie enthalten sind, und b) Sie können den Overhead tolerieren (die Ausnahmebehandlung ist mit einem Overhead verbunden.) Das heißt, using finallyist nur unter diesen Bedingungen gültig. Es gibt sehr seltene, aber gültige Situationen, in denen ein Goto der richtige Weg ist.
Luis.espinal
21
Okay Leute ... was zum Teufel ist der Unterschied zwischen break 3oder break myLabelund goto myLabel. Oh, das ist richtig, nur das Schlüsselwort. Wenn Sie verwenden break, continueoder einige ähnliche Keyword Sie sind infact mit einem goto(Wenn Sie verwenden for, while, foreach, do/loopSie einen bedingten goto verwenden.)
Matthew Whited
4
Niedrige Leistung - Das Verhalten eines modernen Prozessors ist möglicherweise schwer vorhersehbar (Pipeline, nicht ordnungsgemäße Ausführung), sodass es sich in 99% der Fälle möglicherweise nicht lohnt oder sogar langsamer ist. @MatthewWhited: Umfang. Wenn Sie an einer beliebigen Stelle in den Gültigkeitsbereich eintreten, ist (für den Menschen) nicht klar, welche Konstruktoren aufgerufen werden sollen (das Problem besteht auch in C).
Maciej Piechotka
42

Übergeordnete Kontrollflusskonstrukte tendieren dazu, Konzepten in der Problemdomäne zu entsprechen. Ein if / else ist eine Entscheidung, die auf einer bestimmten Bedingung basiert. Eine Schleife fordert Sie auf, einige Aktionen wiederholt auszuführen. Sogar eine break-Anweisung besagt, "wir haben das wiederholt gemacht, aber jetzt müssen wir aufhören".

Eine goto-Anweisung entspricht dagegen eher einem Konzept im laufenden Programm, nicht in der Problemdomäne. Es heißt, die Ausführung an einer bestimmten Stelle im Programm fortzusetzen . Jemand, der den Code liest, muss ableiten, was dies in Bezug auf die Problemdomäne bedeutet.

Natürlich können alle übergeordneten Konstrukte in Form von gotos und einfachen bedingten Verzweigungen definiert werden. Das heißt nicht, dass sie nur verkleidet sind. Betrachten Sie sie als eingeschränkte GOTOS - und es sind die Einschränkungen, die sie nützlich machen. Eine break-Anweisung wird als Sprung an das Ende der einschließenden Schleife implementiert, aber es ist besser, sie als Operation für die gesamte Schleife zu betrachten.

Wenn alles andere gleich ist, ist Code, dessen Struktur die der Problemdomäne widerspiegelt, in der Regel leichter zu lesen und zu warten.

Es gibt keine Fälle, in denen eine goto-Anweisung unbedingt erforderlich ist (es gibt einen entsprechenden Satz ), aber es gibt Fälle, in denen dies die am wenigsten schlechte Lösung sein kann. Diese Fälle variieren von Sprache zu Sprache, je nachdem, welche übergeordneten Konstrukte die Sprache unterstützt.

In C glaube ich zum Beispiel, dass es drei grundlegende Szenarien gibt, in denen ein goto angemessen ist.

  1. Aus einer verschachtelten Schleife ausbrechen. Dies wäre unnötig, wenn die Sprache eine beschriftete break-Anweisung hätte.
  2. Rettung aus einem Codeabschnitt (normalerweise ein Funktionskörper) im Falle eines Fehlers oder eines anderen unerwarteten Ereignisses. Dies wäre unnötig, wenn die Sprache Ausnahmen hätte.
  3. Implementieren einer expliziten Zustandsmaschine. In diesem Fall (und ich denke nur in diesem Fall) entspricht ein goto direkt einem Konzept in der Problemdomäne, das von einem Zustand in einen angegebenen anderen Zustand übergeht, in dem der aktuelle Zustand dargestellt wird, durch den der Codeblock gerade ausgeführt wird .

Andererseits kann eine explizite Zustandsmaschine auch mit einer switch-Anweisung innerhalb einer Schleife implementiert werden. Dies hat den Vorteil, dass jeder Zustand an der gleichen Stelle im Code beginnt, was beispielsweise beim Debuggen hilfreich sein kann.

Die Hauptverwendung eines goto in einer einigermaßen modernen Sprache (eine, die if / else und Schleifen unterstützt) besteht darin, ein Kontrollflusskonstrukt zu simulieren, das in der Sprache fehlt.

Keith Thompson
quelle
5
Dies ist die bisher beste Antwort - ziemlich überraschend für eine anderthalb Jahre alte Frage. :-)
Konrad Rudolph
1
+1 bis "beste Antwort bisher". --- "Die Hauptverwendung eines goto in einer einigermaßen modernen Sprache (eine, die if / else und Schleifen unterstützt) ist die Simulation eines Kontrollflusskonstrukts, das in der Sprache fehlt."
Dave Dopson
In switch-statement state machine ist jedes Schreiben auf den Steuerwert wirklich a goto. SSSMs sind oft gute Strukturen, da sie den SM-Status vom Ausführungskonstrukt trennen, aber "gotos" nicht wirklich eliminieren.
Supercat
2
"Wenn alles andere gleich ist, ist Code, dessen Struktur die der Problemdomäne widerspiegelt, in der Regel leichter zu lesen und zu warten." Ich habe das schon lange vollständig gewusst, aber es fehlten mir die Worte, um es so präzise zu kommunizieren, dass andere Programmierer es tatsächlich verstehen können und werden. Danke dafür.
Wildcard
Das "Theorem der strukturierten Programme" amüsiert mich, da es klar zeigt, dass die Verwendung von strukturierten Programmieranweisungen zur Emulation eines Goto viel weniger lesbar ist als nur die Verwendung eines Goto!
11

Das hängt sicher von der Programmiersprache ab. Der Hauptgrund gotofür die Kontroverse ist, dass der Compiler Sie zu großzügig einsetzen lässt. Probleme können beispielsweise auftreten, wenn Sie so verwenden gotokönnen, dass Sie jetzt auf eine nicht initialisierte Variable zugreifen können, oder noch schlimmer , wenn Sie in eine andere Methode springen und mit dem Aufrufstapel herumspielen. Es sollte in der Verantwortung des Compilers liegen, unsinnigen Kontrollfluss zu verbieten.

Java hat versucht, dieses Problem zu "lösen", indem es dies gotovollständig untersagte . Mit Java können Sie jedoch returninnerhalb eines finallyBlocks eine Ausnahme verwenden, die versehentlich verschluckt wird. Das gleiche Problem besteht immer noch: Der Compiler erledigt seine Arbeit nicht. Das Entfernen gotoaus der Sprache hat das Problem nicht behoben.

In C #, gotoist so sicher wie break, continue, try/catch/finallyund return. Sie können keine nicht initialisierten Variablen verwenden, Sie können nicht aus einem finally-Block usw. herausspringen. Der Compiler beschwert sich. Dies liegt daran, dass es das eigentliche Problem löst , das wie gesagt unsinniger Kontrollfluss ist. gotoBricht die Analyse bestimmter Zuordnungen und andere sinnvolle Compilerprüfungen nicht auf magische Weise ab.

Timwi
quelle
Dein Standpunkt ist, dass C # gotonicht böse ist? Wenn ja, ist das für jede routinemäßig verwendete moderne Sprache mit einem gotoKonstrukt der Fall , nicht nur für C # ...
lvella
9

Ja. Wenn Ihre Schleifen mehrere Ebenen tief verschachtelt sind, gotoist dies die einzige Möglichkeit, elegant aus einer inneren Schleife auszubrechen. Die andere Option besteht darin, ein Flag zu setzen und aus jeder Schleife auszubrechen, wenn dieses Flag eine Bedingung erfüllt. Das ist wirklich hässlich und ziemlich fehleranfällig. In diesen Fällen gotoist es einfach besser.

Natürlich macht Javas beschriftete breakAnweisung dasselbe, aber ohne dass Sie zu einem beliebigen Punkt im Code springen können, wodurch das Problem ordentlich gelöst wird, ohne dass die Dinge zugelassen werden, die das gotoBöse machen .

Chinmay Kanchi
quelle
Möchte jemand die Ablehnung erklären? Dies war eine zutreffende Antwort auf die Frage ...
Chinmay Kanchi
4
goto ist niemals eine elegante Antwort. Wenn Ihre Schleifen mehrere Ebenen tief verschachtelt sind, besteht das Problem darin, dass Sie Ihren Code nicht entworfen haben, um die mehrfach verschachtelten Schleifen zu vermeiden. Prozeduraufrufe sind NICHT der Feind. (Lesen Sie die Papiere "Lambda: The Ultimate ...".) Wenn Sie mit einem Goto mehrere Ebenen tief verschachtelte Schleifen durchbrechen, wird eine offene Mehrfachfraktur mit einem Pflaster versehen: Es ist zwar einfach, aber nicht richtig Antworten.
John R. Strohm
6
Und natürlich gibt es nie den einen oder anderen Fall, in dem tief verschachtelte Schleifen erforderlich sind? Als guter Programmierer müssen Sie nicht nur die Regeln einhalten, sondern auch wissen, wann Sie sie brechen müssen.
Chinmay Kanchi
2
@ JohnR.Strohm Sag niemals nie. Wenn Sie beim Programmieren Begriffe wie "nie" verwenden, haben Sie sich eindeutig nicht mit Eckfällen, Optimierung, Ressourcenbeschränkungen usw. befasst. In tief verschachtelten Schleifen ist goto in der Tat die sauberste und eleganteste Art, die Schleifen zu verlassen. Es spielt keine Rolle, wie sehr Sie Ihren Code überarbeitet haben.
Sujay Phadke
@ JohnR.Strohm: Zweithäufigstes Feature beim Umschalten von VB.NET auf C #: die DoSchleife. Warum? Weil ich die eine Schleife, die eine tiefe Pause braucht, in eine DoSchleife und Typ ändern kann Exit Dound es keine gibt GoTo. Verschachtelte Schleifen passieren die ganze Zeit. Das Brechen der Stapel geschieht in einigen Prozent der Fälle.
Joshua
7

Der größte Teil der Entmutigung kommt von einer Art "Religion", die von Gott Djikstra geschaffen wurde und die in den frühen 60er Jahren zwang, über ihre wahllose Macht zu sprechen, um:

  • Springe irgendwo in irgendeinen Codeblock
    • Funktion von Anfang an nicht ausgeführt
    • Schleifen werden nicht von Anfang an ausgeführt
    • Variableninitialisierung übersprungen
  • Springen Sie von jedem Codeblock weg, ohne dass eine Bereinigung möglich ist.

Dies hat nichts mehr mit der gotoAussage der modernen Sprachen zu tun , deren Existenz lediglich die Schaffung anderer als der von der Sprache bereitgestellten Codestrukturen unterstützen soll.

Insbesondere ist der erste Hauptpunkt oben nicht mehr erlaubt und der zweite wird gereinigt (wenn Sie gotoaus einem Block heraus sind, wird der Stapel richtig abgewickelt und alle richtigen Destruktoren aufgerufen).

Sie können sich auf diese Antwort beziehen , um eine Vorstellung davon zu bekommen, wie unlesbar Code sein kann, der goto nicht verwendet. Das Problem liegt nicht bei sich selbst, sondern in der schlechten Verwendung.

Ich kann ein ganzes Programm , ohne schreiben if, einfach for. Natürlich ist es nicht gut lesbar, sieht ungeschickt und unnötig kompliziert aus.

Das Problem ist aber nicht for. Da ich bin.

Dinge wie break, continue, throw, bool needed=true; while(needed) {...}usw. unter Hinweis darauf , mehr als Maskerade goto von den Säbeln der Djikstrarian Eiferer zu entkommen weg, dass -50 Jahre nach der Erfindung der modernen laguages- noch ihre Gefangene wollen. Sie vergaßen, wovon Djikstra sprach, sie erinnerten sich nur an den Titel seiner Notiz (GOTO wurde als schädlich eingestuft, und es war nicht einmal sein eigener Titel: Er wurde vom Herausgeber geändert) und beschuldigten und schlugen, schlugen und schlugen jedes Konstrukt, das diese 4 hatte Buchstabe in Reihenfolge gebracht.

Es ist 2011: Es ist Zeit zu verstehen, dass Djikstra gotosich nicht mit der GOTOAussage auseinandersetzen muss, die ihn überzeugt hat.

Emilio Garavaglia
quelle
1
"Dinge wie break, continue, throw, bool needed = true; while (needed) {...} usw. sind nicht mehr als Maskerade goto" Ich stimme dem nicht zu. Ich würde es vorziehen, "Dinge wie Pause usw. sind eingeschränkt, gehe zu" oder so ähnlich. Das Hauptproblem bei goto ist, dass es normalerweise zu mächtig ist. Soweit das aktuelle goto weniger mächtig ist als das von Dijkstra, ist Ihre Antwort für mich in Ordnung.
Muhammad Alkarouri
2
@ Muhammad Alkarouri: Ich stimme vollkommen zu. Sie haben gerade eine bessere Formulierung gefunden, um genau mein Konzept auszudrücken. Mein Punkt ist, dass diese Einschränkung manchmal nicht anwendbar ist und die Art der Einschränkung, die Sie benötigen, nicht in der Sprache ist. Dann ist eine "mächtigere" Sache, die weniger spezialisiert ist, das, was Sie in der Lage macht, Abhilfe zu schaffen. Zum Beispiel ist Java break nin C ++ nicht verfügbar goto escape, selbst wenn escapees nicht erforderlich ist, nur aus der n-ple-Schleife herauszukommen.
Emilio Garavaglia
4

Das seltsame Hin- und Her, solange es sich um eine lokale Funktion handelt, beeinträchtigt die Lesbarkeit nur selten erheblich. Oft wird es dadurch verbessert, dass darauf hingewiesen wird, dass in diesem Code etwas Ungewöhnliches vor sich geht, das die Verwendung einer etwas ungewöhnlichen Kontrollstruktur erfordert.

Wenn (lokale) gotos die Lesbarkeit erheblich beeinträchtigen, ist dies normalerweise ein Zeichen dafür, dass die Funktion, die das goto enthält, zu komplex geworden ist.

Das letzte, was ich in einen C-Code geschrieben habe, war, ein Paar ineinandergreifender Schleifen zu erstellen. Es passt nicht in die normale Definition von "akzeptabler" Verwendung, aber die Funktion wurde dadurch deutlich kleiner und klarer. Um dies zu vermeiden, wäre eine besonders unordentliche Verletzung von DRY erforderlich gewesen.

blucz
quelle
1
Die Antwort darauf findet sich am ehesten im alten Slogan "Lay's Potato Chips": "Ich wette, du kannst nicht nur einen essen". In Ihrem Beispiel haben Sie zwei "ineinandergreifende" Schleifen (was auch immer das bedeutet, und ich wette, es ist nicht hübsch), und das goto hat Ihnen ein billiges Out gegeben. Was ist mit dem Instandhalter, der diesen Code ändern muss, nachdem Sie von einem Bus angefahren wurden? Wird das Goto sein Leben leichter oder schwerer machen? Müssen diese beiden Loops überarbeitet werden?
John R. Strohm
3

Ich denke, diese ganze Angelegenheit war ein Fall, in dem der falsche Baum angebellt wurde.

GOTO als solches scheint mir kein Problem zu sein, aber es ist sehr oft ein Symptom einer tatsächlichen Sünde: Spaghetti-Code.

Wenn das GOTO eine größere Überschneidung von Flusskontrolllinien verursacht, ist es schlecht, Punkt. Wenn es keine Flusskontrolllinien kreuzt, ist es harmlos. In der Grauzone dazwischen haben wir Dinge wie Loop-Rettungsaktionen. Es gibt immer noch einige Sprachen, die keine Konstrukte hinzugefügt haben, die alle legitimen Graufälle abdecken.

Der einzige Fall, in dem ich es in vielen Jahren tatsächlich verwendet habe, ist der Fall der Schleife, in der sich der Entscheidungspunkt in der Mitte der Schleife befindet. Sie haben entweder doppelten Code, eine Flagge oder ein GOTO. Ich finde die GOTO-Lösung die beste der drei. Es gibt hier kein Überkreuzen von Strömungskontrolllinien, es ist harmlos.

Loren Pechtel
quelle
Der Fall, auf den Sie anspielen, wird manchmal als "Schleife und eine halbe" oder "N plus eine halbe Schleife" bezeichnet, und es ist ein berühmter Anwendungsfall für gotos, die in eine Schleife springen (wenn die Sprache dies zulässt; seit dem anderen Fall wird gesprungen aus der Schleife in der Mitte, ist in der Regel ohne trivial goto).
Konrad Rudolph
(Leider verbieten die meisten Sprachen das Springen in eine Schleife.)
Konrad Rudolph
@KonradRudolph: Wenn Sie die "Schleife" mit GOTO implementieren, gibt es keine andere Schleifenstruktur, in die Sie springen können.
Loren Pechtel
1
Ich denke , der gotoals Schreien der Steuerungsablauf hier nicht passt NORMAL structured-PROGRAMMIER PATTERNS . Da Kontrollabläufe, die zu strukturierten Programmiermustern passen, einfacher zu überlegen sind als solche, die dies nicht tun, sollte man versuchen, solche Muster zu verwenden, wenn dies möglich ist. Wenn Code zu solchen Mustern passt, sollte man nicht schreien, dass dies nicht der Fall ist. Auf der anderen Seite ist es in Fällen, in denen Code wirklich nicht zu solchen Mustern passt, besser, darüber zu schreien, als vorzutäuschen, dass der Code passt, wenn dies wirklich nicht der Fall ist.
Supercat
1

Ja, das goto kann verwendet werden, um die Erfahrung des Entwicklers zu verbessern : http://adamjonrichardson.com/2012/02/06/long-live-the-goto-statement/

Wie bei jedem leistungsfähigen Tool (Zeiger, Mehrfachvererbung usw.) muss man jedoch diszipliniert damit umgehen. Das in dem Link bereitgestellte Beispiel verwendet PHP, wodurch die Verwendung des goto-Konstrukts auf dieselbe Funktion / Methode beschränkt wird und das Springen in einen neuen Steuerblock (z. B. Schleife, switch-Anweisung usw.) deaktiviert wird.

AdamJonR
quelle
1

Kommt auf die Sprache an. Es wird zum Beispiel immer noch häufig in der Cobol-Programmierung verwendet. Ich habe auch an einem Barionet 50-Gerät gearbeitet, dessen Firmware-Programmiersprache ein früher BASIC-Dialekt ist, der natürlich die Verwendung von Goto erfordert.

user16764
quelle
0

Ich würde nein sagen. Wenn Sie die Notwendigkeit finden, GOTO zu verwenden, müssen Sie den Code wahrscheinlich neu gestalten.

Walter
quelle
3
Vielleicht, aber Sie haben keine Argumentation angegeben
Casebash
Dijkstra lieferte vor Jahrzehnten die ausführliche Begründung.
John R. Strohm
2
Nur ein Dogma. Keine Gründe. Anmerkung: Djikstra ist NICHT MEHR EINE GÜLTIGE ENTSCHEIDUNG: Es bezog sich auf die BASIC- oder FORTRAN GOTO-Anweisung der frühen 60er Jahre. Sie haben nichts mit den wirklich anämischen (verglichen mit der Macht dieser) Gots von heute zu tun.
Emilio Garavaglia
0

gotoDies kann nützlich sein, wenn Sie älteren Assembler-Code nach C portieren. In erster Linie kann eine anweisungsweise Konvertierung nach C gotoals Ersatz für die branchAnweisung des Assemblers eine sehr schnelle Portierung ermöglichen.

Graham Borland
quelle
-1

Ich würde nicht argumentieren. Sie sollten immer durch ein Tool ersetzt werden, das spezifischer für das zu lösende Problem ist (es sei denn, es ist in Ihrer Sprache nicht verfügbar). Beispielsweise lösen break-Anweisungen und -Ausnahmen Probleme, die zuvor durch Schleifen-Escape- und Fehlerbehandlung behoben wurden.

Fischtoaster
quelle
Ich würde argumentieren, dass Sprachen, die beide Merkmale aufweisen, gotoin diesen Sprachen normalerweise nicht vorhanden sind.
Chinmay Kanchi
Aber ist das so, weil sie sie nicht brauchen, oder weil die Leute denken, dass sie sie nicht brauchen?
Michael K
Ihre Ansichten sind bigott. Es gibt viele Fälle goto, in denen die Problemdomänensemantik am nächsten kommt und alles andere ein schmutziger Hack wäre.
SK-logic
@ SK-logic: Ich denke nicht, dass das Wort "bigott" bedeutet, was Sie denken, dass es bedeutet.
Keith Thompson
1
@Keith, "intolerant gegenüber seinen eigenen Meinungen und Vorurteilen" - das ist es, was ich hier konsequent beobachte, mit diesem Los, bei dem ich die Augen verbunden habe. "Sollte immer ersetzt werden" sind zumindest Fanatikerworte. Nichts, was einer vernünftigen Meinung nahe kommt, sondern nur eine reine Bigotterie. Dieses "immer" lässt keinen Raum für eine alternative Meinung, sehen Sie?
SK-logic