20.6 Nicht-lokale Sprünge – <setjmp.h> 

In C sind Sprünge über Funktionsgrenzen hinweg nicht erlaubt. Das heißt genau: Funktionen werden immer an den direkten Ausrufer zurückgegeben. Wenn z. B. Funktion 1 die Funktion 2 aufruft, kehrt Funktion 2 immer zuerst zur Funktion 1 zurück – eben in der umgekehrten Reihenfolge wie die einzelnen Funktionen auf (genauer: unter) dem Stack abgelegt wurden. Erst dann kann Funktion 1 zu ihrem Aufrufer zurückkehren. Ein Beispiel:
/* call_func.c */ #include <stdio.h> #include <stdlib.h> void func1(void); void func2(void); void func3(void); void func4(void); void func1(void) { printf("Funktion 1 ist aufgerufen!\n"); func2(); } void func2(void) { printf("Funktion 2 ist aufgerufen!\n"); func3(); } void func3(void) { printf("Funktion 3 ist aufgerufen!\n"); func4(); } void func4(void) { printf("Funktion 4 ist aufgerufen!\n"); } int main(void) { func1(); return EXIT_SUCCESS; }
Das Programm ruft in der main()-Funktion zuerst func1() auf, func1() ruft anschließend func2() auf, func2() ruft danach func3() auf, und func3() ruft am Ende func4() auf. Anschließend kehren die einzelnen Funktionen wieder in der Reihenfolge func3(), func2() und func1() zur main()-Funktion zurück. Was wäre jetzt, wenn in func2() eine Berechnung durchgeführt wird und der Wert dieser Berechnung nicht mehr dem entspricht, den der Nutzer sich versprochen hat? Trotzdem werden sinnloserweise noch func3() und func4() aufgerufen und ausgeführt. Die Frage lautet also: Wie kann man z. B. von func2() zur main()-Funktion zurückspringen, die Funktionen func3() und func4() auslassen und auch nicht mehr über func1() zur main()-Funktion zurückkehren?
Abbildung 20.2 Rückkehr von Funktionen bei einem normalen Verlauf
Dafür können Sie die Funktionen der Headerdatei <setjmp.h> verwenden. Hier sehen Sie die Syntax:
#include <setjmp.h> jmp_buf env; // primitiver Datentyp jmp_buf int setjmp(jmp_buf env); void longjmp(jmp_buf env, int wert);
Der Datentyp jmp_buf env ist eine Art Puffer, der den mit setjmp(env) eingefrorenen Programmzustand enthält und den Sie mit der Funktion longjmp(env,1) wiederherstellen können. jmp_buf enthält zum Beispiel die CPU-Registerinhalte (CS, DS, SS und ES), den Stackpointer (SP), den Instruktionspointer (IP) usw. – alle Informationen eben, die erforderlich sind, um den gleichen Zustand wiederherzustellen, der vor dem Aufruf von setjmp() vorlag.
Mit setjmp() werden, wie eben schon erwähnt, alle Informationen, die im Augenblick vorliegen, auf einen Stack gelegt. Der Aufruf von setjmp() lautet:
if(setjmp(env) == 0)
Beim ersten Aufruf von setjmp() liefert die Funktion den Wert 0 zurück. Beim zweiten Aufruf durch longjmp(env) liefert die Funktion auf jeden Fall einen Wert ungleich 0 zurück.
Mit der Funktion longjmp() kehren Sie dann an diese Programmstelle zurück, die Sie mit setjmp(env) auf dem Stack abgelegt haben. Dies geschieht mit folgendem Aufruf:
longjmp(env,1);
Nochmals alles zusammengefasst:
... jmp_buf programmzustand; ... if(setjmp(programmzustand) == 0) printf("Programmzustand auf den Stack gelegt\n"); else printf("Rücksprung mit longjmp erfolgt\n"); ... // viele, viele Funktionen später longjmp(programmzustand,1);
Als Erstes legen Sie hier mit setjmp() den Programmzustand auf den Stack. Anschließend, viele Funktionen später, wird mit longjmp() dieser Zustand wiederhergestellt und springt zurück zu setjmp(). Dieses Mal ist der Rückgabewert von setjmp() aber nicht mehr 0, und daher fährt das Programm hinter der else-Anweisung fort.
Jetzt soll alles in einem Programm verwendet werden, ohne komplizierte Berechnungen oder Ähnliches. Es wird einfach abgefragt, wie viele Funktionen ausgeführt werden sollen, und das Programm springt nach der gewünschten Anzahl der Funktionen mit einem Aufruf von longjmp() zur main()-Funktion zurück:
/* setjmp.c */ #include <stdio.h> #include <stdlib.h> #include <setjmp.h> void func1(int); void func2(int); void func3(int); void func4(void); jmp_buf env; static int zahl; void func1(int zahl) { printf("Funktion 1 ist aufgerufen!\n"); if(zahl == 1) longjmp(env, 1); func2(zahl); } void func2(int zahl) { printf("Funktion 2 ist aufgerufen!\n"); if(zahl == 2) longjmp(env, 2); func3(zahl); } void func3(int zahl) { printf("Funktion 3 ist aufgerufen!\n"); if(zahl == 3) longjmp(env, 3); func4(); } void func4(void) { printf("Funktion 4 ist aufgerufen!\n"); } int main(void) { printf("Wie viele Funktionen sollen ausgefuehrt werden : "); scanf("%d",&zahl); if(setjmp(env) == 0) func1(zahl); else printf("Ruecksprung durch longjmp von Funktion %d!\n",zahl); return EXIT_SUCCESS; }
Die Funktionen setjmp() und longjmp() haben übrigens nichts mit der Anweisung goto gemeinsam. Es hat sich als recht nützlich erwiesen, setjmp() und longjmp() bei Fehlerbehandlungen einzusetzen.
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.