The local variables in the function where longjmp() is called are
irrelevant: you're returning from that function so they can't be
accessed anyway (except in the trivial case where longjmp() is called
from the same function invocation as setjmp()). The variables at risk
are those in the function invocation where setjmp() was called.
Indeed.
It may be useful to know the reason for this. A common way to implement
setjmp() is to store the current values of all registers, and restore
them when longjmp() is called. This will restore the stack pointer to
point to the frame that was current when setjmp() was called, so that
variables stored on the stack will be correct. But variables stored
in registers will be restored to their values when setjmp() was called,
losing any changes to them between the setjmp() and longjmp().
Or, equivalently, the implementation might save and restore *only*
those registers critical to locating the correct stack frame (really,
"overall execution context"). On most machines, excluding the x86
that nobody uses

, this avoids a whole lot of often-useless
work, making setjmp() execute noticeably faster.
In either case, to elaborate a bit, consider the following C source
fragment (which I do not claim is any good or even any use; it is
just for illustration, especially the "block1" and "block2" labels):
#include <setjmp.h>
jmp_buf label;
void f(T1 arg1, T2 arg2) {
while (someloop) {
if (somecond) {
register int x;
block1:
x = 42;
... some code here ...
if (setjmp(label)) {
printf("jumped back, in block 1, x = %d\n", x);
goto block2;
}
x += 3;
... more code here, calling g() ...
}
if (othercond) {
register int y;
block2:
... still more code here, calling h() ...
}
}
}
Suppose that the compiler uses the *same* machine register for both
x and y. This is a perfectly reasonable thing to do, since the
two variables have separate scopes. When "x" disappears, "y"
appears, and vice versa: perfect conditions for sharing.
Now, when -- in your function that uses longjmp(), say inside g()
-- you execute a "goto label" (via longjmp(label), which is just
a particularly powerful kind of "go to"), this will either restore
x to the saved value, if the implementation saves all the registers;
or it will fail to restore x.
Suppose it chooses to restore x to the saved value. In this case,
if you were inside block1, all is somewhat well, except that x has
"magically" (via the restore) had the 3 subtracted back out. That
is, the flow of control was as follows:
- enter block 1
- set x to 42
- call setjmp
. save the point of the call
. save the register holding x (42)
. return 0
- add 3 to x (now it is 45)
- execute "more code":
. call some function g()
. longjmp(label):
* restore x (to 42)
* terminate function g() and resume f() at the "if (setjmp(label))"
(making setjmp() return some nonzero value this time)
- print x (42), which apparently has not had 3 added.
On the other hand, if the implementation chooses *not* to restore
x, it *may* still have 45 in it. This would be even better, since
this would make x act like a regular variable. Also, it is "faster",
because the setjmp and longjmp do not have to modify the register.
But what if "g" had a "register int z" that used the same register?
Then x would quite likely "magically" acquire z's value.
Now, if you were to get out of the first block in f() safely, and
get into block2, and call h() and have h() longjmp back to f(),
and print x there, this would either restore the saved value (42),
or leave the register alone. Of course, such code is technically
undefined -- you left the block that defined x and then re-entered
it (via the goto that longjmp() does) -- so you should not expect
any particular value anyway. But now let me modify the original
code a bit, to remove the sub-block for x. I will also remove the
"register" keyword, since it is just a hint anyway.
void f(T1 arg1, T2 arg2) {
int x;
while (someloop) {
if (somecond) {
block1:
x = 42;
... some code here ...
if (setjmp(label)) {
printf("jumped back, in block 1, x = %d\n", x);
goto block2;
}
x += 3;
... more code here, calling g() ...
}
if (othercond) {
int y;
block2:
... still more code here, calling h() ...
}
}
}
Now if h() calls longjmp(label), the effect is "almost defined"
(with the word "almost" inserted because of the "volatile" issue
being described here), because the variable x is defined way up
at the top of f().
Suppose, however, our compiler is a decent one, that optimizes
well. It notices that x is only used in block1; and of course
y is only used in block2. It therefore realizes that it can use
a single machine register to hold *both* variables, just as we did
"manually" in the first version of the code.
This time, if we longjmp() from h(), and setjmp() and longjmp() do
*not* save and restore the machine register, we can get the value
that was in y printed when we reach the printf(). Of course, if
the implementation's setjmp()/longjmp() save and restore the
register, we get 42 -- the "x += 3" has again "magically vanished".
The standard, as usual, does not say that this particular "incorrect"
behaviour will occur; it just says that such any variables that
*might* be in registers are undefined.
And of course:
Potentially, any time when it isn't declared volatile. ...
So this means that you have to write f() above as:
void f(T1 arg1, T2 arg2) {
volatile int x;
while (someloop) {
if (somecond) {
block1:
x = 42;
... some code here ...
if (setjmp(label)) {
printf("jumped back, in block 1, x = %d\n", x);
goto block2;
}
x += 3;
... more code here, calling g() ...
}
if (othercond) {
int y;
block2:
... still more code here, calling h() ...
}
}
}
That way, whether g() or h() (or anything they call) use longjmp
to go back to the "label", x will have a predictable value -- 45
-- and everything will be guaranteed to work.
Note that, in this *particular* case, "y" need not be declared
volatile, because block2 cannot use it without first setting it
to some value. It is of course safe to use "volatile" on y too,
but it is not required (and may inhibit some optimization).
Your best bet is usually to avoid longjmp() entirely, because it
is a very powerful "goto", and "goto" is best used sparingly, if
at all. There are restrictions on where you can use setjmp() and
longjmp(), but compilers rarely enforce them, and if you accidentally
misuse them, not only does your program usually crash, many debuggers
get confused in the process and cannot help you pinpoint the problem.
(The reason is that longjmp() often just "sets" the machine's stack
and/or frame pointer registers, and if the values it sets are
garbage, the debugger has no clue where to look for the problem.
Worse, after setting these registers, longjmp just "sets" the
program-counter / instruction-pointer register, eradicating all
traces of its own handiwork. So you get a mysterious failure at
the jumped-to point, with no record of *why* you got to that point.
All the debugger can tell you is "the stack frames are all trashed,
and the program was executing in this apparently-randomly-chosen
function or data area, at the time the crash occurred.")