Ich verstehe, dass außer zum Ausbrechen von in Schleifen verschachtelten Schleifen; Die goto
Aussage wird als fehleranfälliger Programmierstil umgangen und verleumdet, um niemals verwendet zu werden.
Alt Text: "Neal Stephenson findet es niedlich, seine Labels 'dengo' zu nennen"
Siehe den Original-Comic unter: http://xkcd.com/292/
Weil ich das früh gelernt habe; Ich habe keine wirklichen Einsichten oder Erfahrungen darüber, zu welchen Arten von Fehlern sie goto
tatsächlich führen. Also, worüber reden wir hier:
- Instabilität?
- Nicht wartbarer oder nicht lesbarer Code?
- Sicherheitslücken?
- Noch etwas ganz anderes?
Zu welchen Fehlern führen "goto" -Anweisungen tatsächlich? Gibt es historisch bedeutsame Beispiele?
Antworten:
Hier ist eine Möglichkeit, es zu betrachten, die ich in den anderen Antworten noch nicht gesehen habe.
Es geht um Umfang. Eine der wichtigsten Säulen einer guten Programmierpraxis ist es, den Rahmen eng zu halten. Sie brauchen engen Spielraum, weil Sie nicht die geistige Fähigkeit haben, mehr als nur ein paar logische Schritte zu überwachen und zu verstehen. Sie erstellen also kleine Blöcke (von denen jeder zu "einer Sache" wird) und bauen dann mit diesen größere Blöcke, die zu einer Sache werden, und so weiter. So bleiben die Dinge überschaubar und nachvollziehbar.
Ein goto erweitert effektiv den Umfang Ihrer Logik auf das gesamte Programm. Dies wird Ihr Gehirn mit Sicherheit für alle Programme außer den kleinsten, die nur wenige Zeilen umfassen, besiegen.
Sie können also nicht mehr erkennen, ob Sie einen Fehler begangen haben, da es einfach zu viel gibt, um nach Ihrem armen kleinen Kopf zu suchen. Dies ist das eigentliche Problem, Fehler sind nur eine wahrscheinliche Folge.
quelle
Es ist nicht so, dass das an
goto
sich schlecht ist. (Immerhin ist jeder Sprungbefehl in einem Computer ein Goto.) Das Problem ist, dass es einen menschlichen Programmierstil gibt, der eine strukturierte Programmierung vorwegnimmt, was als "Flussdiagramm" -Programmierung bezeichnet werden könnte.In der Ablaufdiagrammprogrammierung (die Menschen meiner Generation gelernt haben und für das Apollo Moon-Programm verwendet wurden) erstellen Sie ein Diagramm mit Blöcken für Anweisungsausführungen und Rauten für Entscheidungen, und Sie können sie mit überall verlaufenden Linien verbinden. (Sogenannter "Spaghetti-Code".)
Das Problem mit dem Spaghetti-Code ist, dass Sie als Programmierer "wissen" können, dass es richtig ist, aber wie können Sie es sich selbst oder anderen beweisen ? Tatsächlich kann es tatsächlich zu einem möglichen Fehlverhalten kommen, und Ihr Wissen, dass es immer richtig ist, kann falsch sein.
Es folgte eine strukturierte Programmierung mit Anfangs- und Endblöcken, z. B. if-else und so weiter. Diese hatten den Vorteil, dass Sie immer noch alles in ihnen tun können, aber wenn Sie überhaupt vorsichtig waren, können Sie sicher sein, dass Ihr Code korrekt war.
Natürlich können die Leute auch ohne Spaghetti-Code schreiben
goto
. Die übliche Methode besteht darin, ein zu schreibenwhile(...) switch( iState ){...
, wobei unterschiedliche Fälle dieiState
Werte unterschiedlich festlegen. Tatsächlich könnte man in C oder C ++ ein Makro schreiben, um das zu tun, und es benennenGOTO
, alsogoto
ist es eine Unterscheidung ohne Unterschied , zu sagen, dass Sie nicht verwenden .Als Beispiel dafür, wie Code-Proving uneingeschränkt ausschließen kann
goto
, bin ich vor langer Zeit auf eine Kontrollstruktur gestoßen, die für sich dynamisch ändernde Benutzeroberflächen nützlich ist. Ich nannte es differenzielle Ausführung . Es ist voll Turing-universal, aber seine Richtigkeit Beweis hängt von reiner strukturierter Programmierung - neingoto
,return
,continue
,break
, oder Ausnahmen.quelle
Warum ist es gefährlich?
goto
verursacht keine Instabilität von selbst. Trotz etwa 100.000goto
s ist der Linux-Kernel immer noch ein Stabilitätsmodell.goto
an sich sollte keine Sicherheitslücken verursachen. In einigen Sprachen kann das Mischen mittry
/catch
exception-Verwaltungsblöcken jedoch zu Schwachstellen führen, wie in dieser CERT-Empfehlung erläutert . Mainstream-C ++ - Compiler kennzeichnen und verhindern solche Fehler, ältere oder exotischere Compiler jedoch nicht.goto
verursacht unlesbaren und nicht wartbaren Code. Dies wird auch als Spaghetti-Code bezeichnet , da es wie bei einer Spaghetti-Platte sehr schwierig ist, den Kontrollfluss zu verfolgen, wenn zu viele GOTOS vorhanden sind.Selbst wenn Sie es schaffen, Spaghetti-Code zu vermeiden, und wenn Sie nur ein paar GOTOS verwenden, können Fehler wie und Ressourcenlecks auftreten:
goto
Aussage brechen Sie diesen direkten Fluss und brechen die Erwartungen. Beispielsweise werden Sie möglicherweise nicht bemerken, dass Sie noch Ressourcen freigeben müssen.goto
an verschiedenen Orten können Sie zu einem einzelnen Ziel senden. Daher ist es nicht offensichtlich, dass Sie genau wissen, in welchem Zustand Sie sich befinden, wenn Sie diesen Ort erreichen. Das Risiko, falsche / unbegründete Annahmen zu treffen, ist daher recht groß.Zusätzliche Informationen und Zitate:
E.Dijkstra schrieb bereits 1968 einen frühen Aufsatz über das Thema: " Go To Statement als schädlich "
Brian.W.Kernighan & Dennis.M.Ritchie haben in der Programmiersprache C geschrieben:
James Gosling & Henry McGilton schrieben 1995 in ihrem Whitepaper zur Java-Umgebung :
Bjarne Stroustrup definiert goto in seinem Glossar mit diesen einladenden Begriffen:
Wann kann verwendet werden?
Wie K & R bin ich nicht dogmatisch in Bezug auf Gotos. Ich gebe zu, dass es Situationen gibt, in denen man sich das Leben erleichtern könnte.
In der Regel ermöglicht goto in C das Verlassen einer mehrstufigen Schleife oder die Fehlerbehandlung, wobei ein geeigneter Austrittspunkt erreicht werden muss, der alle bisher zugewiesenen Ressourcen freigibt / entsperrt (eine mehrfache Zuweisung in Folge bedeutet mehrere Bezeichnungen). In diesem Artikel werden die verschiedenen Verwendungen von goto im Linux-Kernel quantifiziert.
Persönlich bevorzuge ich es zu vermeiden und in 10 Jahren von C habe ich maximal 10 gotos verwendet. Ich bevorzuge geschachtelte
if
s, die meiner Meinung nach besser lesbar sind. Wenn dies zu einer zu tiefen Verschachtelung führen würde, würde ich entweder meine Funktion in kleinere Teile zerlegen oder einen Booleschen Indikator in einer Kaskade verwenden. Die heutigen Compiler zur Optimierung sind clever genug, um fast den gleichen Code wie mit demselben Code zu generierengoto
.Die Verwendung von goto hängt stark von der Sprache ab:
In C ++ führt die ordnungsgemäße Verwendung von RAII dazu, dass der Compiler Objekte, die außerhalb des Gültigkeitsbereichs liegen, automatisch zerstört, sodass die Ressourcen / Sperren ohnehin bereinigt werden und keine Notwendigkeit mehr besteht, weiterzugehen.
In Java gibt es keine Notwendigkeit für goto (siehe Java des Autor Zitat oben und diese ausgezeichnete Stack - Überlaufes Antwort ): der Garbage Collector, der das Chaos reinigt,
break
,continue
undtry
/catch
Ausnahmebehandlung alles den Fall abzudecken , wogoto
hilfreich sein könnte, aber in einem sichereren und besser Weise. Javas Popularität beweist, dass man in einer modernen Sprache eine goto-Aussage vermeiden kann.Zoomen Sie auf die berühmte Sicherheitsanfälligkeit " SSL goto fail"
Wichtiger Haftungsausschluss: Angesichts der heftigen Diskussion in den Kommentaren möchte ich klarstellen, dass ich nicht vorgebe, die goto-Anweisung sei die einzige Ursache für diesen Fehler. Ich behaupte nicht, dass es ohne goto keinen Bug geben würde. Ich möchte nur zeigen, dass ein Goto in einen schwerwiegenden Fehler verwickelt sein kann.
Ich weiß nicht, wie viele schwerwiegende Fehler
goto
in der Geschichte der Programmierung vorkommen: Details werden oft nicht mitgeteilt. Es gab jedoch einen berühmten Apple SSL-Fehler , der die Sicherheit von iOS beeinträchtigte. Die Aussage, die zu diesem Fehler führte, war eine falschegoto
Aussage.Einige argumentieren, dass die Hauptursache des Fehlers nicht die goto-Anweisung an sich war, sondern ein falsches Kopieren / Einfügen, ein irreführender Einzug, fehlende geschweifte Klammern um den bedingten Block oder möglicherweise die Arbeitsgewohnheiten des Entwicklers. Ich kann keines von ihnen bestätigen: Alle diese Argumente sind wahrscheinliche Hypothesen und Interpretationen. Niemand weiß es wirklich. (In der Zwischenzeit scheint die in den Kommentaren vorgeschlagene Hypothese einer fehlerhaften Zusammenführung angesichts einiger anderer Inkonsistenzen bei Einrückungen in derselben Funktion ein sehr guter Kandidat zu sein ).
Die einzige objektive Tatsache ist, dass ein Duplikat dazu
goto
geführt hat, die Funktion vorzeitig zu verlassen. Betrachtet man den Code, wäre die einzige andere Anweisung, die den gleichen Effekt hätte erzielen können, eine Rückkehr gewesen.Der Fehler ist
SSLEncodeSignedServerKeyExchange()
in dieser Datei in Funktion :In der Tat hätten geschweifte Klammern um den bedingten Block den Fehler verhindern können:
Es hätte entweder zu einem Syntaxfehler beim Kompilieren (und damit zu einer Korrektur) oder zu einem redundanten harmlosen goto geführt. Übrigens könnte GCC 6 diese Fehler dank seiner optionalen Warnung zur Erkennung inkonsistenter Einrückungen erkennen.
Aber in erster Linie hätten alle diese Goto mit strukturiertem Code vermieden werden können. Goto ist also zumindest indirekt eine Ursache für diesen Bug. Es gibt mindestens zwei verschiedene Möglichkeiten, um dies zu vermeiden:
Ansatz 1: if-Klausel oder verschachtelte
if
sAnstatt viele Bedingungen nacheinander auf Fehler zu prüfen und
fail
im Problemfall jedes Mal an ein Etikett zu senden , hätte man sich dafür entscheiden können, die kryptografischen Operationen in einerif
-Anweisung auszuführen, die dies nur dann tun würde, wenn es keine falsche Vorbedingung gäbe:Ansatz 2: Verwenden Sie einen Fehlerakku
Dieser Ansatz basiert auf der Tatsache, dass fast alle Anweisungen hier eine Funktion aufrufen, um einen
err
Fehlercode festzulegen, und den Rest des Codes nur ausführen, wennerr
0 war (dh die Funktion wurde ohne Fehler ausgeführt). Eine schöne sichere und lesbare Alternative ist:Hier gibt es kein einziges Goto: Kein Risiko, schnell zum Ausfallausgangspunkt zu springen. Und visuell wäre es einfach, eine falsch ausgerichtete Linie oder eine vergessene zu erkennen
ok &&
.Dieses Konstrukt ist kompakter. Es basiert auf der Tatsache, dass in C der zweite Teil eines logischen und (
&&
) nur ausgewertet wird, wenn der erste Teil wahr ist. Tatsächlich entspricht der von einem optimierenden Compiler erzeugte Assembler fast dem ursprünglichen Code mit gotos: Der Optimierer erkennt die Kette von Bedingungen sehr gut und generiert Code, der beim ersten Rückgabewert ungleich Null ans Ende springt ( online proof ).Sie können sich sogar eine Konsistenzprüfung am Ende der Funktion vorstellen, mit der während der Testphase Unstimmigkeiten zwischen dem OK-Flag und dem Fehlercode festgestellt werden können.
Fehler wie ein
==0
versehentlich durch einen!=0
oder logische Konnektorfehler werden während der Debugging-Phase leicht erkannt.Wie gesagt: Ich behaupte nicht, dass alternative Konstrukte jeden Fehler vermieden hätten. Ich möchte nur sagen, dass sie das Auftreten des Fehlers erschweren könnten.
quelle
goto
geschweifte Klammern stehtif
. Aber wenn Sie vorschlagen, dass das Vermeidengoto
Ihren Code gegen die Auswirkungen von zufällig duplizierten Zeilen immun macht, habe ich schlechte Nachrichten für Sie.if
Anweisung? Lustige Zeiten. :)Der berühmte Artikel von Dijkstra wurde zu einer Zeit geschrieben, als einige Programmiersprachen tatsächlich in der Lage waren, Unterprogramme mit mehreren Ein- und Ausstiegspunkten zu erstellen . Mit anderen Worten, Sie könnten buchstäblich in die Mitte einer Funktion springen und an einer beliebigen Stelle innerhalb der Funktion herausspringen, ohne diese Funktion aufzurufen oder auf herkömmliche Weise zurückzukehren. Das gilt immer noch für die Assemblersprache. Niemand argumentiert jemals, dass ein solcher Ansatz der strukturierten Methode zum Schreiben von Software, die wir heute verwenden, überlegen ist.
In den meisten modernen Programmiersprachen werden Funktionen sehr spezifisch mit einem Einstiegs- und einem Ausstiegspunkt definiert . Am Einstiegspunkt geben Sie die Parameter für die Funktion an und rufen sie auf. Am Ausstiegspunkt geben Sie den resultierenden Wert zurück und setzen die Ausführung gemäß der Anweisung fort, die dem ursprünglichen Funktionsaufruf folgt.
Innerhalb dieser Funktion sollten Sie in der Lage sein, im Rahmen der Vernunft alles zu tun, was Sie wollen. Wenn die Funktion durch ein oder zwei Punkte klarer oder schneller wird, warum nicht? Der springende Punkt einer Funktion ist es, ein bisschen klar definierte Funktionalität zu binden, so dass Sie nicht mehr darüber nachdenken müssen, wie sie intern funktioniert. Sobald es geschrieben ist, benutzt du es einfach.
Und ja, Sie können innerhalb einer Funktion mehrere return-Anweisungen haben. Es gibt immer noch eine Stelle in einer richtigen Funktion, von der aus Sie zurückkehren (im Grunde genommen die Rückseite der Funktion). Das ist keineswegs dasselbe wie das Herausspringen aus einer Funktion mit einem Goto, bevor sie die Chance hat, ordnungsgemäß zurückzukehren.
Es geht also nicht wirklich um die Verwendung von goto's. Es geht darum, ihren Missbrauch zu vermeiden. Alle sind sich einig, dass Sie mit gotos ein furchtbar kompliziertes Programm erstellen können, aber Sie können dasselbe auch tun, indem Sie Funktionen missbrauchen (es ist einfach viel einfacher, gotos zu missbrauchen).
Seit ich von BASIC-Programmen im Zeilennummern-Stil zu strukturiertem Programmieren mit Pascal und geschweiften Klammern übergegangen bin, musste ich kein Goto mehr verwenden. Das einzige Mal, dass ich versucht war, eine zu verwenden, war das vorzeitige Verlassen einer verschachtelten Schleife (in Sprachen, die kein mehrstufiges vorzeitiges Verlassen von Schleifen unterstützen), aber ich kann normalerweise einen anderen Weg finden, der sauberer ist.
quelle
Ich habe
goto
als Kind beim Schreiben von BASIC-Programmen Anweisungen verwendet, um auf einfache Weise For- und While-Schleifen zu erhalten (Commodore 64 BASIC hatte keine While-Schleifen, und ich war zu unreif, um die richtige Syntax und Verwendung von For-Schleifen zu lernen ). Mein Code war häufig trivial, aber alle Schleifenfehler konnten sofort auf meine Verwendung von goto zurückgeführt werden.Ich verwende jetzt hauptsächlich Python, eine Programmiersprache auf hohem Niveau, die festgestellt hat, dass keine Notwendigkeit für goto besteht.
Als Edsger Dijkstra 1968 erklärte, "Goto als schädlich", gab er nicht eine Handvoll Beispiele an, bei denen verwandte Bugs dem goto angelastet werden könnten, sondern erklärte, dies
goto
sei für höhere Sprachen unnötig und sollte zugunsten dessen vermieden werden Wir betrachten heute den normalen Kontrollfluss: Schleifen und Bedingungen. Seine Worte:Er hatte wahrscheinlich Berge von Beispielen von Fehlern, die jedes Mal auftraten, wenn er Code darin debuggte
goto
. Sein Beitrag war jedoch eine verallgemeinerte Stellungnahme, die durch Beweisegoto
untermauert war, die für höhere Sprachen nicht erforderlich waren. Der allgemeine Fehler ist, dass Sie möglicherweise nicht in der Lage sind, den fraglichen Code statisch zu analysieren.Code ist mit
goto
Anweisungen viel schwieriger statisch zu analysieren , insbesondere wenn Sie in Ihrem Kontrollfluss (den ich früher durchgeführt habe) oder in einem nicht verwandten Codeabschnitt zurückspringen. Code kann und wurde so geschrieben. Es wurde darauf geachtet, die sehr knappen Rechenressourcen und damit die Architekturen der Maschinen stark zu optimieren.Ein apokryphales Beispiel
Es gab ein Blackjack-Programm, das ein Betreuer als sehr elegant optimiert empfand, das er jedoch aufgrund der Art des Codes nicht "reparieren" konnte. Es wurde in Maschinencode programmiert, der stark von gotos abhängt - daher halte ich diese Geschichte für ziemlich relevant. Dies ist das beste kanonische Beispiel, das ich mir vorstellen kann.
Ein Gegenbeispiel
Die C-Quelle von CPython (die am häufigsten verwendete und am häufigsten verwendete Python-Referenzimplementierung) verwendet jedoch
goto
Anweisungen mit großer Wirkung. Sie werden verwendet, um irrelevante Steuerungsabläufe in Funktionen zu umgehen, um zum Ende der Funktionen zu gelangen und die Funktionen effizienter zu gestalten, ohne die Lesbarkeit zu verlieren. Dies respektiert eines der Ideale der strukturierten Programmierung - einen einzigen Austrittspunkt für Funktionen zu haben.Ich halte dieses Gegenbeispiel für recht einfach, statisch zu analysieren.
quelle
Als das Wigwam aktuelle architektonische Kunst war, konnten ihre Erbauer Ihnen zweifellos fundierte praktische Ratschläge bezüglich des Baus von Wigwams, wie man Rauch entweichen lässt und so weiter geben. Glücklicherweise können die heutigen Bauherren wahrscheinlich die meisten dieser Ratschläge vergessen.
Als die Postkutsche die aktuelle Transportkunst war, konnten ihre Fahrer Ihnen zweifellos fundierte praktische Ratschläge in Bezug auf Pferde von Postkutschen geben, wie man sich gegen Straßenräuber verteidigt , und so weiter. Glücklicherweise können die heutigen Autofahrer auch die meisten dieser Ratschläge vergessen.
Wenn Lochkarten aktuelle Programmierkunst waren, konnten ihre Praktiker Ihnen ebenfalls fundierte praktische Ratschläge zur Organisation von Karten, zur Nummerierung von Aussagen usw. geben. Ich bin mir nicht sicher, ob dieser Rat heute sehr relevant ist.
Bist du alt genug, um die Phrase " Zahlenaussagen " zu kennen ? Sie müssen es nicht wissen, aber wenn Sie es nicht wissen, sind Sie nicht mit dem historischen Kontext vertraut, in dem die Warnung vor
goto
hauptsächlich relevant war.Die Warnung davor
goto
ist heute einfach nicht sehr relevant. Mit dem Grundlagentraining in while / for-Schleifen und Funktionsaufrufen werden Sie nicht einmal daran denken ,goto
sehr oft eine zu erstellen. Wenn Sie daran denken, haben Sie wahrscheinlich einen Grund, also fahren Sie fort.Aber kann die
goto
Aussage nicht missbraucht werden?Antwort: Sicher, es kann missbraucht werden, aber sein Missbrauch ist ein ziemlich kleines Problem in der Softwareentwicklung im Vergleich zu weitaus häufiger auftretenden Fehlern wie der Verwendung einer Variablen, für die eine Konstante verwendet wird, oder wie dem Ausschneiden und Einfügen (ansonsten) bekannt als Nachlässigkeit gegenüber Refactor). Ich bezweifle, dass Sie in großer Gefahr sind. Wenn Sie die
longjmp
Steuerung nicht verwenden oder anderweitig auf fernen Code übertragen, können Siegoto
sie zum Spaß ausprobieren , wenn Sie die Verwendung von a oder möchten. Es wird Dir gut gehen.Sie können das Fehlen der letzten Horrorgeschichten bemerken, in denen
goto
der Bösewicht spielt. Die meisten oder alle dieser Geschichten scheinen 30 oder 40 Jahre alt zu sein. Sie stehen auf festem Grund, wenn Sie diese Geschichten als größtenteils veraltet ansehen.quelle
Um eine Sache zu den anderen ausgezeichneten Antworten hinzuzufügen, kann es mit
goto
Aussagen schwierig sein, genau zu sagen, wie Sie an eine bestimmte Stelle im Programm gelangt sind. Sie können wissen, dass in einer bestimmten Zeile eine Ausnahme aufgetreten ist, aber wenn sichgoto
im Code eine Ausnahme befindet, können Sie nicht feststellen, welche Anweisungen ausgeführt wurden, um diesen Ausnahmezustand zu verursachen, ohne das gesamte Programm zu durchsuchen. Es gibt keinen Aufrufstapel und keinen visuellen Fluss. Es ist möglich, dass es eine Anweisung gibt, die 1000 Zeilen entfernt ist und Sie in einen schlechten Zustand versetzt, indem eine Anweisunggoto
an die Zeile ausgeführt wird, die eine Ausnahme ausgelöst hat.quelle
On Error Goto errh
, können Sie erneutResume
versuchen,Resume Next
die fehlerhafte Zeile überspringen undErl
wissen, um welche Zeile es sich handelt (wenn Sie Zeilennummern verwenden).Resume
Führt diese Funktion von Anfang an erneut aus undResume Next
überspringt alles in der Funktion, was noch nicht vorhanden ist.On Error Goto 0
und manuelles Debuggen vorübergehend deaktivieren .Resume
ist dafür vorgesehen, nach ÜberprüfungErr.Number
und notwendigen Einstellungen verwendet zu werden.goto
nur mit beschrifteten Linien gearbeitet wird, die anscheinend von beschrifteten Schleifen abgelöst wurden .goto ist für Menschen schwieriger zu verstehen als andere Formen der Flusskontrolle.
Das Programmieren des richtigen Codes ist schwierig. Richtige Programme zu schreiben ist schwierig, festzustellen, ob Programme korrekt sind, ist schwierig, zu beweisen, dass Programme korrekt sind.
Es ist einfach, Code dazu zu bringen, vage das zu tun, was Sie wollen, verglichen mit allem anderen, was mit Programmieren zu tun hat.
goto löst einige Programme, indem Code abgerufen wird, um das zu tun, was Sie wollen. Dies erleichtert nicht die Überprüfung der Richtigkeit, während dies bei Alternativen häufig der Fall ist.
Es gibt Programmierstile, bei denen goto die geeignete Lösung ist. Es gibt sogar Programmierstile, bei denen der böse Zwilling comefrom die geeignete Lösung ist. In beiden Fällen ist äußerste Sorgfalt geboten, um sicherzustellen, dass Sie es in einem verständlichen Muster verwenden, und es muss manuell überprüft werden, ob es nicht schwierig ist, die Richtigkeit sicherzustellen.
Als Beispiel gibt es eine Eigenschaft einiger Sprachen, die Coroutinen genannt werden. Coroutinen sind fadenlose Fäden; Ausführungsstatus ohne Thread, auf dem es ausgeführt werden soll. Sie können sie zur Ausführung auffordern, und sie können einen Teil von sich selbst ausführen und sich dann aussetzen und die Flusskontrolle zurückgeben.
"Flache" Coroutinen in Sprachen ohne Coroutine-Unterstützung (wie C ++ vor C ++ 20 und C) sind mit einer Mischung aus gotos und manueller Zustandsverwaltung möglich. "Tiefe" Coroutinen können mit den
setjmp
undlongjmp
Funktionen von C erstellt werden.Es gibt Fälle, in denen Coroutinen so nützlich sind, dass es sich lohnt, sie manuell und sorgfältig zu schreiben.
Im Fall von C ++ werden sie als so nützlich befunden, dass sie die Sprache erweitern, um sie zu unterstützen. Das
goto
s und manuelle Zustandsmanagement ist hinter einer Null-Kosten-Abstraktionsschicht verborgen, die es Programmierern ermöglicht, sie zu schreiben, ohne die Schwierigkeit zu haben, zu beweisen, dass das Durcheinander von goto,void**
s, manuellem Aufbau / Zerstörung des Zustands usw. korrekt ist.Das Goto verbirgt sich hinter einer übergeordneten Abstraktion, wie
while
oderfor
oderif
oderswitch
. Diese übergeordneten Abstraktionen lassen sich leichter als richtig erweisen und überprüfen.Wenn in der Sprache einige fehlten (wie in einigen modernen Sprachen Koroutinen fehlen), wird entweder das Limpling zusammen mit einem Muster, das nicht zum Problem passt, oder die Verwendung von goto zu Ihrer Alternative.
Es ist einfach, einen Computer dazu zu bringen, vage das zu tun, was Sie möchten . Zuverlässig robusten Code zu schreiben ist schwierig . Springen hilft dem Ersten weitaus mehr als dem Zweiten; Daher wird es als schädlich eingestuft, da es ein Zeichen für oberflächlich "arbeitenden" Code mit tiefen und schwer auffindbaren Fehlern ist. Mit ausreichendem Aufwand kann man Code mit "goto" zuverlässig robust machen, so dass es falsch ist, die Regel als absolut zu halten; aber als Faustregel ist es eine gute.
quelle
longjmp
In C ist es nur zulässig, den Stapelsetjmp
in eine übergeordnete Funktion zurückzuspulen. (Wie das Ausbrechen aus mehreren Verschachtelungsebenen, aber für Funktionsaufrufe anstelle von Schleifen).longjjmp
Essetjmp
ist nicht legal , einen Kontext mit einer Funktion zu erstellen, die seitdem zurückgegeben wurde (und ich vermute, dass dies in den meisten Fällen in der Praxis nicht funktioniert). Ich bin nicht mit Co-Routinen vertraut, aber aus Ihrer Beschreibung einer Co-Routine, die einem User-Space-Thread ähnelt, gehe ich davon aus, dass sie einen eigenen Stack sowie einen Kontext für gespeicherte Register benötigt undlongjmp
nur ein Register enthält -sparend, kein separater Stapel.Werfen Sie einen Blick auf diesen Code, von http://www-personal.umich.edu/~axe/research/Software/CC/CC2/TourExec1.1.f.html , die tatsächlich Teil eines großen Gefangenendilemma Simulation war. Wenn Sie alten FORTRAN- oder BASIC-Code gesehen haben, werden Sie feststellen, dass dies nicht ungewöhnlich ist.
Hier gibt es viele Probleme, die weit über die GOTO-Aussage hinausgehen. Ich denke ehrlich, die GOTO-Aussage war ein bisschen ein Sündenbock. Der Kontrollfluss ist hier jedoch absolut nicht klar, und der Code ist so gemischt, dass sehr unklar ist, was vor sich geht. Auch ohne das Hinzufügen von Kommentaren oder die Verwendung besserer Variablennamen würde eine Änderung in eine Blockstruktur ohne GOTOs das Lesen und Verfolgen erheblich erleichtern.
quelle
Eines der Prinzipien der wartbaren Programmierung ist die Kapselung . Der Punkt ist, dass Sie eine Schnittstelle mit einem Modul / einer Routine / einer Unterroutine / einer Komponente / einem Objekt unter Verwendung einer definierten Schnittstelle und nur dieser Schnittstelle herstellen und die Ergebnisse vorhersehbar sind (vorausgesetzt, die Einheit wurde effektiv getestet).
Innerhalb einer einzigen Codeeinheit gilt das gleiche Prinzip. Wenn Sie konsequent strukturierte Programmierung oder objektorientierte Programmierprinzipien anwenden, werden Sie nicht:
Zu den häufigsten Symptomen dieser Verarbeitungsfehler gehören Speicherverluste, Speicherhorting, Zeigerüberläufe, Abstürze, unvollständige Datensätze, Ausnahmen beim Hinzufügen / Ändern / Löschen von Datensätzen, Speicherseitenfehler usw.
Die vom Benutzer beobachtbaren Manifestationen dieser Probleme umfassen die Sperrung der Benutzeroberfläche, eine allmählich abnehmende Leistung, unvollständige Datensätze, die Unfähigkeit, Transaktionen zu initiieren oder abzuschließen, beschädigte Daten, Netzwerkausfälle, Stromausfälle und Ausfälle eingebetteter Systeme (aufgrund des Verlustes der Kontrolle über Raketen) auf den Verlust der Verfolgungs- und Kontrollfähigkeit in Flugsicherungssystemen), Zeitgeberausfälle und so weiter und so fort. Der Katalog ist sehr umfangreich.
quelle
goto
einmal zu erwähnen . :)goto ist gefährlich, weil es normalerweise dort verwendet wird, wo es nicht benötigt wird. Alles, was nicht benötigt wird, ist gefährlich, aber besonders wichtig . Wenn Sie googeln, werden Sie viele Fehler finden, die durch goto verursacht werden. Dies ist per se kein Grund, es nicht zu verwenden (Fehler treten immer auf, wenn Sie Sprachfunktionen verwenden, da dies für die Programmierung von wesentlicher Bedeutung ist), aber einige davon hängen eindeutig eng zusammen zu goto Nutzung.
Gründe für die Verwendung / Nichtverwendung von goto :
Wenn Sie eine Schleife benötigen, müssen Sie
while
oder verwendenfor
.Wenn Sie einen bedingten Sprung ausführen müssen, verwenden Sie
if/then/else
Wenn Sie eine Prozedur benötigen, rufen Sie eine Funktion / Methode auf.
Wenn Sie eine Funktion beenden müssen, genügt es
return
.Ich kann an meinen Fingern die Stellen zählen, an denen ich sie gesehen
goto
habe und richtig benutzt habe.In libKTX gibt es eine Funktion mit dem folgenden Code
Jetzt an dieser Stelle
goto
ist nützlich, weil die Sprache C ist:Dieser Anwendungsfall ist nützlich, da in C keine Klassen vorhanden sind. Die einfachste Methode zum Bereinigen ist daher die Verwendung von a
goto
.Wenn wir den gleichen Code in C ++ hatten, brauchen wir nicht mehr zu gehen:
Zu was für Fehlern kann es führen? Etwas. Endlosschleifen, falsche Reihenfolge der Ausführung, Stapelschraube ..
Ein dokumentierter Fall ist eine Sicherheitsanfälligkeit in SSL, die es Man in the middle-Angriffen ermöglicht, die durch die falsche Verwendung von goto verursacht werden. Hier ist der Artikel
Dies ist ein Tippfehler, der jedoch eine Zeit lang unbemerkt blieb. Wenn der Code auf andere Weise strukturiert wäre und ordnungsgemäß getestet worden wäre, wäre ein solcher Fehler möglicherweise nicht möglich gewesen.
quelle