Sprunganweisung
Eine Sprunganweisung oder ein Sprungbefehl ist eine Anweisung in einer Programmiersprache. In Computerprogrammen dient sie dazu, die Verarbeitung nicht mit dem nachfolgenden Befehl, sondern an einer beliebigen Stelle fortzuführen.
Bekannte Sprunganweisungen
Die bekannteste Sprunganweisung ist das sogenannte Goto (von englisch go to gehe zu, nach). Dies ist in der Programmierung ein Befehl im Programmcode, der einen Sprung zu einer anderen Stelle im Programm bewirkt.
Verzweigungen zu Unterprogrammen (auch: Subroutine und Function) werden mittels Call oder ähnlich lautenden Befehlen (je nach Programmiersprache und Unterprogrammtyp) ausgeführt.
Ein Rücksprung ist die Rückkehr zu der Stelle, an der das (Haupt-)Programm nach einem solchen Aufruf und der Ausführung eines Unterprogramms fortzusetzen ist – häufig veranlasst durch eine Return-Anweisung im Unterprogramm.
Jedoch wird auch der Sprung zurück an eine schon mal besuchte Stelle im Programmcode, also die Wiederholung eines Programmabschnitts in einer Schleife, als Rücksprung bezeichnet. Anstelle von Goto-Anweisungen werden in modernen Programmierverfahren sogenannte Kontrollstrukturen verwendet; Details siehe unten.
Sprunganweisungen auf Maschinenebene
Im Unterschied zu den Hochsprachen spricht man hier häufiger von Sprungbefehlen als von Sprunganweisungen.
Prozessoren kennen in der Regel mehrere verschiedene Sprungbefehle. Diese kann man einteilen in:
- unbedingt oder bedingt
- Der Sprung wird entweder immer ausgeführt oder nur wenn bestimmte Flags im Statusregister bestimmte, geforderte Werte haben. Um welche Werte und Flags es sich handelt ist dabei in der Sprunganweisung codiert (Beispiel: JNZ bedeutet Jump (if) Not Zero).
- absolut, relativ, Register oder Speicher
- Das Programm wird an der angegebenen Adresse fortgeführt.
- Es erfolgt der Sprung relativ zur aktuellen Position. Damit ist verschiebbarer (relozierbarer) Code möglich.
- Die Zieladresse steht in einem Register
JMP reg
. - Die Adresse des Sprungziels steht im Speicher
JMP [reg]
oderJMP [reg+offs]
.
Eine besondere Form von Sprungbefehlen sind Unterprogrammaufrufe, d. h. Sprünge mit Rückkehrabsicht. Bei deren Ausführung wird zuerst die Adresse des Folgebefehls gesichert (auf dem Stack oder in einem Register),[1] erst dann wird an die Zieladresse gesprungen. Über Rücksprungbefehle kann das Unterprogramm wieder an die ursprüngliche Position zurückkehren und das Programm so fortgesetzt werden. Die Rücksprungadresse wird vom Stack entnommen oder steht in einem Register.
Sprunganweisungen auf Hochsprachenebene
In höheren Programmiersprachen ist eine Sprunganweisung eine Anweisung der Form
goto Markenname
oder
if Bedingung goto Markenname
Im ersten Fall wird sie unbedingte Sprunganweisung, in zweiten bedingte Sprunganweisung genannt. Dabei steht Markenname
für den Namen einer Sprungmarke, die an einer anderen Stelle des Programms definiert ist. Bei der Abarbeitung des Programms wird an der Stelle fortgesetzt, an der die Sprungmarke steht.
Im Prinzip könnte man allein mit bedingten Sprunganweisungen und einigen Zusatzfunktionen für Datenein- und -ausgabe jeden Algorithmus programmieren. Die entstehenden Programme sind allerdings ab einer gewissen Größe nahezu unwartbar. In aktuellen Programmiersprachen werden explizit formulierte Sprunganweisungen deshalb selten verwendet. Stattdessen werden häufiger höherwertige Kontrollstrukturen, insbesondere Schleifen, Unterprogrammaufrufe und bedingt auszuführende Programmblöcke, eingesetzt, in denen die logisch dazugehörenden Verzweigungs- und Rückverzweigungsbefehle implizit, d. h. vom Übersetzer im Maschinencode eingefügt werden, ohne dass ein Goto programmiert werden muss.
GOTO-Varianten
In der maschinennahen Programmierung dient der Goto-Befehl dazu, zu einer anderen Stelle im Programm zu verzweigen, entweder unbedingt oder von einer Bedingung abhängig. In manchen Assemblersprachen heißt der entsprechende Goto-Befehl JMP (englisch jump Sprung) oder BR (englisch branch verzweigen).
In höheren Programmiersprachen (die ggf. auch eine Kleinschreibung erlauben) wird durch einen Goto-Befehl entweder eine Code-Zeilennummer (zum Beispiel in alten BASIC-Dialekten) oder eine definierte Sprungmarke (Label, zum Beispiel in C oder Pascal) angesprochen.
Beispiel eines einfachen Programms mit Goto in Pascal:
PROGRAM beispiel;
LABEL 10;
BEGIN
10:
writeln('Endlosschleife');
GOTO 10;
END.
Das folgende Programmbeispiel (aus dem Essay Coding Style von Linus Torvalds)[2] zeigt die Verwendung eines Goto-Befehls goto out
in C:
int fun(int a) {
int result = 0;
char *buffer = kmalloc(SIZE);
if (buffer == NULL)
return -ENOMEM;
if (condition1) {
while (loop1) {
// ...
}
result = 1;
goto out;
}
// ...
out:
kfree(buffer);
return result;
}
In frühen Varianten älterer Programmiersprachen wie Fortran oder BASIC stellten Goto-Befehle noch die wichtigste, teilweise sogar die einzige Möglichkeit zur Steuerung des Programmflusses dar; diese wurden deshalb auch zur Realisierung von Schleifen und bedingter Ausführung verwendet. Dies führte zu Spaghetticode.
Mit der Programmiersprache ALGOL wurden 1960 eigene Kontrollstrukturen wie while
, for
, if-else
zur Programmierung von Schleifen und bedingten Anweisungen und Verzweigungen eingeführt. Diese Neuerungen wurden bald in andere Programmiersprachen übernommen. So machen while
als Ersatz für Rücksprünge und if
als Ersatz für Vorwärtssprünge bei geeigneter Umstrukturierung eines jeden Programms die Goto-Anweisung theoretisch überflüssig.
Die strukturierte Programmierung und die GOTO-Programmierung sind jedoch aus Sicht der theoretischen Informatik äquivalent; alle solchen Programme fallen in die Kategorie der GOTO-Programme.
Auch in anderer Hinsicht sind die Befehle der strukturierten Programmierung mit dem GOTO äquivalent: Der Befehlssatz eines Prozessors enthält in der Regel keine explizite Unterstützung von Konstrukten wie while
, repeat
, for
, if-else
etc. Daher werden diese bei der Kompilierung eines Programms vom Compiler mittels unbedingter und bedingter Sprunganweisungen (Mnemonik: JP, JMP, Jcond, BR, Bcc, …) nachgebildet. Die genannten Konstrukte bieten also keine neuen Möglichkeiten, sie sind jedoch sinnvoll, um lesbaren und damit wartungsfreundlicheren Code zu forcieren.
Umstrittene Verwendung von GOTO
1968 sprach sich Edsger W. Dijkstra in seinem Aufsatz Go To Statement Considered Harmful (ursprünglich: A Case against the GO TO Statement; der spätere Titel geht auf Niklaus Wirth zurück), für eine Abschaffung des Goto-Befehls in allen höheren Programmiersprachen aus.[3] Das ist ohne weiteres möglich, da nahezu jedes Goto durch sogenannte Kontrollstrukturanweisungen wie do
, for
oder while
ersetzt werden kann (siehe auch Simulation GOTO durch WHILE). Nach anfänglichem Widerstand wurde diese Meinung in der Programmierausbildung bald zum Dogma erhoben; in einigen Sprachen wie Java wurde bewusst überhaupt kein Goto-Befehl eingeführt, jedoch ersatzweise ein labeled break und später ein labeled continue. Dennoch ist goto
als Schlüsselwort auch in Java reserviert. Evtl. behält man sich die Implementierung vor, wahrscheinlich aber auch aus praktischen Gründen. Ansonsten wäre goto ein gültiger Bezeichner. Andererseits unterstützen zahlreiche neue imperative Programmiersprachen noch Goto, z. B. C++, und selbst das 2000 entwickelte C# sowie das 2007 publizierte D. 2009 wurde Goto nachträglich auch in die Skriptsprache PHP ab Version 5.3 eingeführt und war in der neuen Sprache Go enthalten.
Eine weniger kritisierte Variante des Goto-Befehls ist das vorzeitige Verlassen eines Unterprogramms durch return
, das Abbrechen einer Schleife durch break
oder Abbrechen eines bestimmten Schleifendurchlaufs und den Beginn des nächsten Durchlaufs durch continue
. Trotzdem gilt es in einigen Programmiersprachen als guter Programmierstil, wenn ein Block genau einen Einsprungpunkt und genau einen Ausstiegspunkt hat. In Sprachen, welche Ausnahmebehandlungen erlauben, wird dagegen die Ein-Ausstiegspunkt-Regel als obsolet angesehen, da throw
-Anweisungen die Argumentation hinter dieser Regel ad absurdum führen.
In der Praxis hat sich jedoch gezeigt, dass der Verzicht auf Goto zwar möglich ist, jedoch in einigen Fällen zu sehr aufwändigen Konstrukten führt.[4] So hält Donald E. Knuth Goto für die optimale Lösung einiger üblicher Programmierprobleme.[5] Besonders in zeitkritischen Programmteilen ist ein Goto deutlich effizienter, als am Ende von mehreren geschachtelten Schleifen jeweils eine Abbruchprüfung durchzuführen.
Von einigen Entwicklern wurde auf der Linux-Kernel-Mailing-Liste die häufige Verwendung von Goto im Quellcode von Linux diskutiert. Linus Torvalds sagte dabei, dass die Verwendung von Goto die Lesbarkeit des Quellcodes in vielen Fällen sogar deutlich erhöhen könne.[6]
Siehe auch
Weblinks
- Edsger W. Dijkstra: Go To Statement Considered Harmful. In: Communications of the ACM. Vol. 11, No. 3, März 1968, S. 147–148 (PDF)
- David R. Tribble: Go To Statement Considered Harmful: A Retrospective. 27. November 2005
Einzelnachweise
- ↑ IBM System/360 Principles of Operation. (PDF) IBM, 1964, S. 64, abgerufen am 2. Februar 2015 (englisch, Befehlsbeschreibung zu „Branch and Link“: BAL, BALR).
- ↑ Linux kernel coding style, Chapter 7: Centralized exiting of functions (englisch)
- ↑ Edsger W. Dijkstra: Letters to the editor: Go To Statement Considered Harmful. In: Communications of the ACM. Band 11, Nr. 3, März 1968, S. 147–148, doi:10.1145/362929.362947.
- ↑ Frank Rubin: “GOTO Considered Harmful” Considered Harmful. In: ACM (Hrsg.): Communications of the ACM. Band 30, Nr. 3, März 1987, S. 195–196, doi:10.1145/214748.315722 (web.archive.org [PDF; 208 kB; abgerufen am 11. September 2021]).
- ↑ Donald E. Knuth: Structured Programming with go to Statements. In: ACM (Hrsg.): Computing Surveys. Band 6, Nr. 4, 1974, S. 261–301, doi:10.1145/356635.356640 (Online [PDF; 2,9 MB; abgerufen am 11. September 2021]).
- ↑ Linux: Using goto In Kernel Code. (Nicht mehr online verfügbar.) kerneltrap.org, archiviert vom Original am 28. November 2005; abgerufen am 21. September 2010 (englisch).