Reputation: 35448
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?)
Upvotes: 4
Views: 2160
Reputation: 70482
Another approach is along the lines of:
int a, olda;
memcpy(&olda, &a, sizeof(a));
a = 7;
//...
memcpy(&a, &olda, sizeof(a));
// a is "uninitialized"
This avoids the trap representation issues of using assignment, relying on the fact that char
does not have any trap representations. It also benefits from being vastly simpler than using setjmp()
and longjmp()
.
Upvotes: 0
Reputation: 70482
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.
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_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 thelongjmp
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 correspondingsetjmp
macro that do not have volatile-qualified type and have been changed between thesetjmp
invocation andlongjmp
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
.
Upvotes: 8
Reputation: 697
I am curious why you want to do that. However did you try following:
void some_fun() {
int a;
int b = a; // Hoping compiler does not discard this.
a = 7;
// do something
a = b;
}
Upvotes: 0
Reputation: 48507
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
}
Upvotes: 6