You can use setjmp()
and longjmp()
to get the behavior you want, with some rearrangement of your code. The code below initializes a
to 1 so that the print statements do not invoke undefined behavior.
jmp_buf jb;
void some_func (void)
{
int a = 1;
if (setjmp(jb) == 0) {
a = 7;
// do something
printf("a = %d (initialized)\n", a);
// use longjmp to make `a` not initialized
longjmp(jb, 1);
// NOTREACHED
} else {
printf("a = %d (not initialized)\n", a);
}
}
The longjmp()
call returns back to the saved context of setjmp()
, and moving to the else
case means that a
had not been initialized.
When compiled with GCC with optimizations, the above function outputs:
a = 7 (initialized)
a = 1 (not initialized)
If you want this behavior without optimizations enabled, try adding the register
storage class to a
's declaration.
A demo.
A longer explanation
So, why did I think setjmp()
and longjmp()
would work? This is what C.11 §7.13 ¶1-2 has to say about it:
The header <setjmp.h>
defines the macro setjmp
, and declares one function and
one type, for bypassing the normal function call and return discipline.
The type declared is
jmp_buf
which is an array type suitable for holding the information needed to restore a calling
environment. The environment of a call to the setjmp
macro consists of information
sufficient for a call to the longjmp
function to return execution to the correct block and
invocation of that block, were it called recursively. It does not include the state of the
floating-point status flags, of open files, or of any other component of the abstract
machine.
This explains that what is supposed to happen is that a longjmp
back to the context saved in the jmp_buf
by a call to setjmp
will act as if the code that ran up until the longjmp
call was a recursive function call, the the longjmp
acts like a return from that recursive call back the setjmp
. To me, this implies that the automatic variable would be "uninitialized".
int a;
// the following expression will be false if returning from `longjmp`
if (setjmp(jb) == 0) {
// this section of code can be treated like the `setjmp` call induced
// a recursive call on this function that led to the execution of the
// code in this body
a = 7;
//...
// assuming not other code modified `jb`, the following call will
// jump back to the `if` check above, but will behave like a recursive
// function call had returned
longjmp(jb, 1);
} else {
// `a` expected to be uninitialized here
}
But, there seems to be a catch. From C.11 §7.13.2 ¶3:
All accessible objects have values, and all other components of the abstract machine
have state, as of the time the longjmp
function was called, except that the values of
objects of automatic storage duration that are local to the function containing the
invocation of the corresponding setjmp
macro that do not have volatile-qualified type
and have been changed between the setjmp
invocation and longjmp
call are
indeterminate.
Since a
is local, is not volatile-qualified, and has been changed between setjmp
and longjmp
calls, its value is indeterminate, even if it was properly initialized before calling setjmp
!
So, using longjmp
back to a local setjmp
after an automatic non-volatile variable has been modified will always result in making those modified variables "uninitialized" after returning to the point of the setjmp
.