There is a misunderstanding about what accessible variables are restored and unchanged.
The phrase in reference is:
Upon return to the scope of setjmp
, all accessible objects, floating-point status flags, and other components of the abstract machine have the same values as they had when std::longjmp
was executed, except for the non-volatile local variables in setjmp
's scope, whose values are indeterminate if they have been changed since the setjmp
invocation.
When longjmp
is called with a jmp_buf
previously set by a call to setjmp
whose calling frame is still active in the call stack, part of processor state such as some of the registers and flags are restored from data saved in the jmp_buf
and control is transferred back to the return address of the setjmp
call, discarding all intermediary frames without calling any destructors or finalisation code. The only objects that are affected are non-volatile local variables in the setjmp
scope, which may have been restored from the jmp_buf
data or not, and thus have indeterminate values.
Here is an example:
#include <stdio.h>
#include <setjmp.h>
jmp_buf buf;
int v1;
volatile int v123 = 123, v456 = 456;
void willjmp(void) {
longjmp(buf, 42);
}
int main(void) {
volatile int v2;
int val, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12;
// initial values
v1 = v123; v2 = v123; v3 = v123; v4 = v123;
v5 = v123; v6 = v123; v7 = v123; v8 = v123;
v9 = v123; v10 = v123; v11 = v123; v12 = v123;
if ((val = setjmp(buf)) != 0) {
printf("\nafter longjmp:\n");
printf("val=%d\n", val); // prints val=42
printf("v1=%d\n", v1); // prints v1=456
printf("v2=%d\n", v2); // prints v2=456
// non volatile local variables have indeterminate values
printf("v3..v12=%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
v3, v4, v5, v6, v7, v8, v9, v10, v11, v12);
} else {
printf("initial values:\n");
printf("val=%d\n", val); // prints val=42
printf("v1=%d\n", v1); // prints v1=123
printf("v2=%d\n", v2); // prints v2=123
// prints v3..v12=123,123,123,123,123,123,123,123,123,123
printf("v3..v12=%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
v3, v4, v5, v6, v7, v8, v9, v10, v11, v12);
v1 = v456; v2 = v456; v3 = v456; v4 = v456;
v5 = v456; v6 = v456; v7 = v456; v8 = v456;
v9 = v456; v10 = v456; v11 = v456; v12 = v456;
printf("\nbefore longjmp:\n");
printf("v1=%d\n", v1); // prints v1=456
printf("v2=%d\n", v2); // prints v2=456
// prints v3..v12=456,456,456,456,456,456,456,456,456,456
printf("v3..v12=%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
v3, v4, v5, v6, v7, v8, v9, v10, v11, v12);
willjmp();
}
return 0;
}
Output:
initial values:
val=0
v1=123
v2=123
v3..v12=123,123,123,123,123,123,123,123,123,123
before longjmp:
v1=456
v2=456
v3..v12=456,456,456,456,456,456,456,456,456,456
after longjmp:
val=42
v1=456
v2=456
v3..v12=123,123,123,123,123,123,123,123,123,123
Depending on the processor architecture, the compiler implementation and configuration, especially optimisation flags, the output for v3
through v16
could be 456
or 123
or possibly even something else. The output above was produced on my M2 laptop with optimisations enabled. With optimisations disabled, the last line becomes v3..v12=456,456,456,456,456,456,456,456,456,456
.
As can be seen on Goldbolt's Compiler Explorer, different compilers produce different output, for example gcc -O
outputs v3..v12=123,123,123,123,123,123,456,456,456,456