Sometimes when your program detects an unusual situation inside a deeply
nested set of function calls, you would like to be able to immediately
return to an outer level of control. This section describes how you can
do such non-local exits using the setjmp
and longjmp
functions.
As an example of a situation where a non-local exit can be useful, suppose you have an interactive program that has a "main loop" that prompts for and executes commands. Suppose the "read" command reads input from a file, doing some lexical analysis and parsing of the input while processing it. If a low-level input error is detected, it would be useful to be able to return immediately to the "main loop" instead of having to make each of the lexical analysis, parsing, and processing phases all have to explicitly deal with error situations initially detected by nested calls.
(On the other hand, if each of these phases has to do a substantial amount of cleanup when it exits--such as closing files, deallocating buffers or other data structures, and the like--then it can be more appropriate to do a normal return and have each phase do its own cleanup, because a non-local exit would bypass the intervening phases and their associated cleanup code entirely. Alternatively, you could use a non-local exit but do the cleanup explicitly either before or after returning to the "main loop".)
In some ways, a non-local exit is similar to using the `return' statement to return from a function. But while `return' abandons only a single function call, transferring control back to the point at which it was called, a non-local exit can potentially abandon many levels of nested function calls.
You identify return points for non-local exits calling the function
setjmp
. This function saves information about the execution
environment in which the call to setjmp
appears in an object of
type jmp_buf
. Execution of the program continues normally after
the call to setjmp
, but if a exit is later made to this return
point by calling longjmp
with the corresponding jmp_buf
object, control is transferred back to the point where setjmp
was
called. The return value from setjmp
is used to distinguish
between an ordinary return and a return made by a call to
longjmp
, so calls to setjmp
usually appear in an `if'
statement.
Here is how the example program described above might be set up:
#include <setjmp.h> #include <stdlib.h> #include <stdio.h> jmp_buf main_loop; void abort_to_main_loop (int status) { longjmp (main_loop, status); } int main (void) { while (1) if (setjmp (main_loop)) puts ("Back at main loop...."); else do_command (); } void do_command (void) { char buffer[128]; if (fgets (buffer, 128, stdin) == NULL) abort_to_main_loop (-1); else exit (EXIT_SUCCESS); }
The function abort_to_main_loop
causes an immediate transfer of
control back to the main loop of the program, no matter where it is
called from.
The flow of control inside the main
function may appear a little
mysterious at first, but it is actually a common idiom with
setjmp
. A normal call to setjmp
returns zero, so the
"else" clause of the conditional is executed. If
abort_to_main_loop
is called somewhere within the execution of
do_command
, then it actually appears as if the same call
to setjmp
in main
were returning a second time with a value
of -1
.
So, the general pattern for using setjmp
looks something like:
if (setjmp (buffer)) /* Code to clean up after premature return. */ ... else /* Code to be executed normally after setting up the return point. */ ...
Here are the details on the functions and data structures used for performing non-local exits. These facilities are declared in `setjmp.h'.
jmp_buf
hold the state information to
be restored by a non-local exit. The contents of a jmp_buf
identify a specific place to return to.
setjmp
stores information about the
execution state of the program in state and returns zero. If
longjmp
is later used to perform a non-local exit to this
state, setjmp
returns a nonzero value.
setjmp
that
established that return point. Returning from setjmp
by means of
longjmp
returns the value argument that was passed to
longjmp
, rather than 0
. (But if value is given as
0
, setjmp
returns 1
).
There are a lot of obscure but important restrictions on the use of
setjmp
and longjmp
. Most of these restrictions are
present because non-local exits require a fair amount of magic on the
part of the C compiler and can interact with other parts of the language
in strange ways.
The setjmp
function is actually a macro without an actual
function definition, so you shouldn't try to `#undef' it or take
its address. In addition, calls to setjmp
are safe in only the
following contexts:
Return points are valid only during the dynamic extent of the function
that called setjmp
to establish them. If you longjmp
to
a return point that was established in a function that has already
returned, unpredictable and disastrous things are likely to happen.
You should use a nonzero value argument to longjmp
. While
longjmp
refuses to pass back a zero argument as the return value
from setjmp
, this is intended as a safety net against accidental
misuse and is not really good programming style.
When you perform a non-local exit, accessible objects generally retain
whatever values they had at the time longjmp
was called. The
exception is that the values of automatic variables local to the
function containing the setjmp
call that have been changed since
the call to setjmp
are indeterminate, unless you have declared
them volatile
.
In BSD Unix systems, setjmp
and longjmp
also save and
restore the set of blocked signals; see section 21.7 Blocking Signals. However,
the POSIX.1 standard requires setjmp
and longjmp
not to
change the set of blocked signals, and provides an additional pair of
functions (sigsetjmp
and sigsetjmp
) to get the BSD
behavior.
The behavior of setjmp
and longjmp
in the GNU library is
controlled by feature test macros; see section 1.3.4 Feature Test Macros. The
default in the GNU system is the POSIX.1 behavior rather than the BSD
behavior.
The facilities in this section are declared in the header file `setjmp.h'.
jmp_buf
, except that it can also store state
information about the set of blocked signals.
setjmp
. If savesigs is nonzero, the set
of blocked signals is saved in state and will be restored if a
siglongjmp
is later performed with this state.
longjmp
except for the type of its state
argument. If the sigsetjmp
call that set this state used a
nonzero savesigs flag, siglongjmp
also restores the set of
blocked signals.
Go to the first, previous, next, last section, table of contents.