在C / C ++中取消初始化变量

时间:2014-08-26 09:46:12

标签: c++ c initialization

这是一个理论问题而不是实际问题,但我想知道是否可以在C(或C ++)中取消初始化变量。所以我们假设我们有以下代码:

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  
}

(不,我不想在a中放一个随机值,因为那也是一个初始化,也不是0,因为这是一个非常好的值,...我只是希望它再次在那个“我对此一无所知”的阶段,它是在初始化之前的阶段。)

(是的我知道:What happens to a declared, uninitialized variable in C? Does it have a value?

4 个答案:

答案 0 :(得分:7)

您可以使用setjmp()longjmp()来获取所需的行为,并重新安排代码。下面的代码将a初始化为1,以便print语句不会调用未定义的行为。

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);
    }
}

longjmp()调用会返回setjmp()的已保存上下文,而移至else的情况意味着a尚未初始化。

使用GCC进行优化编译时,上述函数输出:

a = 7 (initialized)
a = 1 (not initialized)

如果您希望在未启用优化的情况下执行此操作,请尝试将register存储类添加到a的声明中。

A demo.

更长的解释

那么,为什么我认为setjmp()longjmp()会起作用?这就是C.​​11§7.13¶1-2对它的评价:

  

标题<setjmp.h>定义宏setjmp,并声明一个函数和   一种类型,用于绕过正常的函数调用和返回规则。

  声明的类型是

  jmp_buf

  这是一种适合保存恢复呼叫所需信息的数组类型   环境。 调用setjmp宏的环境包含信息   足以调用longjmp函数将执行返回到正确的块和   调用该块,是否以递归方式调用。它不包括该状态   浮点状态标志,打开文件或抽象的任何其他组件   机。

这解释了应该发生的事情是longjmp通过调用jmp_buf回到setjmp中保存的上下文,就好像代码一直运行到longjmp调用是一个递归函数调用,longjmp就像从递归调用返回setjmp一样。对我来说,这意味着自动变量将是“未初始化的”。

    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
    }

但是,似乎有一个问题。从C.11§7.13.2¶3:

  

所有可访问的对象都具有值以及抽象机器的所有其他组件   有状态,截至调用longjmp函数时,除了的值   自动存储持续时间的对象,它们是包含该函数的函数的本地对象   调用没有volatile限定类型的相应setjmp宏   并且在setjmp调用和longjmp调用之间进行了更改   不确定

由于a是本地的,不符合规范,并且在setjmplongjmp次来电之间进行了更改,因此其值不确定, 即使它在调用setjmp 之前已正确初始化!

因此,在修改了自动非易失性变量后,将longjmp返回到本地setjmp将始终导致在返回{{{点}之后使这些修改后的变量“未初始化” 1}}。

答案 1 :(得分:6)

您可以使用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
}

答案 2 :(得分:0)

我很好奇你为什么要那样做。但是你试过以下:

void some_fun() {
   int a;
   int b = a;  // Hoping compiler does not discard this.

   a = 7;

   // do something
   a = b;
}   

答案 3 :(得分:0)

另一种方法是:

int a, olda;
memcpy(&olda, &a, sizeof(a));

a = 7;
//...

memcpy(&a, &olda, sizeof(a));
// a is "uninitialized"

这避免了使用赋值的陷阱表示问题,依赖于char没有任何陷阱表示的事实。它还比使用setjmp()longjmp()简单得多。