This is more a theoretical question than a practical one, but I was wondering whether is it possible to un-initialize a variable in C (or C++). So let's say we have the following code:
void some_fun()
{
int a; // <-- Here a is an un-initialized variable, it's value is the value
// found in the memory at the given location.
a = 7; // Now I have initialized it
// do something with a ...
// and here do something again with a that it
// will have the same state (ie: indeterministic) as before initialization
}
(No, I don't want to put a random value in a because that would be also an initialization, nor 0, because that's a very nice value, ... I just want it to be again in that "I don't know anything about it" stage it was before initializing it).
(Yes I am aware of: What happens to a declared, uninitialized variable in C? Does it have a value?)
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.
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 macrosetjmp, and declares one function and one type, for bypassing the normal function call and return discipline.The type declared is
jmp_bufwhich is an array type suitable for holding the information needed to restore a calling environment. The environment of a call to the
setjmpmacro consists of information sufficient for a call to thelongjmpfunction 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
longjmpfunction was called, except that the values of objects of automatic storage duration that are local to the function containing the invocation of the correspondingsetjmpmacro that do not have volatile-qualified type and have been changed between thesetjmpinvocation andlongjmpcall 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.
You can emulate this using boost::optional<T>:
#include <boost/optional.hpp>
int main()
{
boost::optional<int> a;
a = 7;
std::cout << a.is_initialized() << std::endl; // true
a.reset(); // "un-initialize"
std::cout << a.is_initialized() << std::endl; // false
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With