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 ):
if (something())
goto fail;
goto fail; // copypasta bug
printf("Never reached\n");
fail:
// control jumps here
Mit vom Compiler unterstützten Ausnahmen können Sie genau denselben Fehler haben, z. B. in C ++:
struct Fail {};
try {
if (something())
throw Fail();
throw Fail(); // copypasta bug
printf("Never reached\n");
}
catch (Fail&) {
// control jumps here
}
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
int computation1() {
return 1;
}
int computation2() {
return computation1();
}
Für einen Compiler ist es genauso schwierig, nicht erreichbaren Code in einem der folgenden 3 Konstrukte zu finden:
void tough1() {
if (computation1() != computation2())
printf("Unreachable\n");
}
void tough2() {
if (computation1() == computation2())
goto out;
printf("Unreachable\n");
out:;
}
struct Out{};
void tough3() {
try {
if (computation1() == computation2())
throw Out();
printf("Unreachable\n");
}
catch (Out&) {
}
}
(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.