4

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?)

Community
  • 1
  • 1
Ferenc Deak
  • 34,348
  • 17
  • 99
  • 167
  • 7
    `a = 7; // Now I have initialized it ` is not initialization. it's assignment. – Jayesh Bhoi Aug 26 '14 at 09:47
  • when your code reach close bracket } you can consider that local "a" variable will become un-initialized – ars Aug 26 '14 at 09:48
  • 3
    You can't do that. Fortunately there is no good reason to do it anyway. – juanchopanza Aug 26 '14 at 09:48
  • @ars but after the closing } I cannot access it ... it's out of scope :( – Ferenc Deak Aug 26 '14 at 09:48
  • `a = NULL` should do it well enough? Unless you're thinking about something else. And also, why do you want to do that? – Yann Aug 26 '14 at 09:48
  • 1
    @juanchopanza That's why I mentioned this to be a theoretical question :) – Ferenc Deak Aug 26 '14 at 09:49
  • 1
    `#define UNINITIALIZE(x)`. Now say `UNINITIALIZE(x);`, and afterwards, `x` has an indeterminate value. It is UB to now read from `x`. – Kerrek SB Aug 26 '14 at 09:49
  • @fritzone, exactly, that's why this is the only variant to "un-initialize" local variable – ars Aug 26 '14 at 09:49
  • 1
    Once you have initialized a variable ,There is no way to uninitialized it again. – pradipta Aug 26 '14 at 09:51
  • You could put another `int a;` inside `{ }` and hope the compiler decides to use the same memory block for it. – downhillFromHere Aug 26 '14 at 09:51
  • I suggest you do not attempt to write source code that is valid C and C++ simultaneously. – pmg Aug 26 '14 at 09:52
  • If you want to do that for performance reasons, the compiler is probably smart enough to recycle variables anyway. Just use another variable. – sbabbi Aug 26 '14 at 09:54
  • @Yann4 Well no, you are most likely setting the int variable to `0` with that. – Jite Aug 26 '14 at 09:57
  • You would never be able to observe the effect of "uninitialisation" in a valid program, so I can't see any reason for it to be possible at all. – molbdnilo Aug 26 '14 at 10:00
  • @molbdnilo in C++ you can read uninitialized values in certain ways without causing undefined behaviour, so it would actually be possible to observe (e.g. if you make multiple reads of the value and it has different values each time). – M.M Aug 26 '14 at 10:14
  • @MattMcNabb Do you have an example of how you could read an uninitialized variable without UB? – juanchopanza Aug 26 '14 at 12:11
  • @juanchopanza oops, I was recalling C , not C++. [See here](http://stackoverflow.com/questions/25074180/is-aa-or-a-a-undefined-behaviour-if-a-is-not-initialized) – M.M Aug 26 '14 at 12:18
  • @fritzone -- your question presumes something that is not true. The initial value of an automatic variable is NOT "the value found in memory at the given location", it is "undefined behavior". There is no guarantee of any predictable behavior for an automatic variable that has not had a value assigned to it. So, NO, you cannot "uninitialize" something that has no defined "uninitialized" behavior. – Bill Weinman Aug 26 '14 at 18:51
  • With some compilers this would be useful, since they may warn when referencing the value of uninitialized variables. So, eg, when a variable is used (some might say misused) as a "local" in a loop, it can be set to "uninitialized" on exit from the loop to prevent it from being referenced until reinitialized. – Hot Licks Sep 04 '14 at 21:20
  • It should be noted that Java and some other languages *require* variables to be initialized before being referenced -- the program cannot be compiled otherwise. In Java's case this is necessary to maintain the strong typing of the language. – Hot Licks Sep 04 '14 at 21:24

4 Answers4

8

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.

jxh
  • 69,070
  • 8
  • 110
  • 193
6

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
}
Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
  • This is also a very good solution, however it's using a C++ class that was designed exactly for this, so it's not really what I was looking for. Regardless of it, the +1 is granted :) – Ferenc Deak Aug 27 '14 at 07:01
0

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;
}   
Amit
  • 677
  • 8
  • 15
0

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().

jxh
  • 69,070
  • 8
  • 110
  • 193