Ferenc Deak
Ferenc Deak

Reputation: 35448

Un-initialize a variable in C/C++

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

Answers (4)

jxh
jxh

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

jxh
jxh

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.

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.

Upvotes: 8

Amit
Amit

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

Piotr Skotnicki
Piotr Skotnicki

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

Related Questions