C程序的局部变量被重用现象

原始问题

起源于《APUE》的习题7.10

int f1(int val) {
    int num = 0;
    int* ptr = #
    if (val == 0) {
        int val = 5;
        ptr = &val;
    }
    return (*ptr + 1);
}

问题是上述代码是否正确?可以看到,如果调用f1(0),那么就会进入if块,定义局部变量val,在离开if块后,val的内存会被自动回收。因为局部变量是定义在栈上的。
参考Automatic_variable

In computer programming, an automatic variable is a local variable which is allocated and deallocated automatically when program flow enters and leaves the variable's scope.

然而实际的测试结果,f1(0)能够成功地输出结果6。
提出疑问,会不会是要过段时间才回收呢?在返回前加上一句sleep(10)后仍然运行正确。使用volatile修饰也没问题,那么不是从寄存器中读取旧的值。

局部变量是C++对象的情况下是否析构

接下来,我使用了C++的类来包装int

struct Integer {
    Integer(int i) : _i(i) { printf("Int(%d)\n", i); }
    ~Integer() { printf("~Int(%d)\n", _i); }

    int _i;
};

int f1(int val) {
    Integer num = 0;
    Integer* ptr = #
    if (val == 0) {
        Integer val = 5;
        ptr = &val;
    }
    return ptr->_i + 1;
}

调用f1(0)的结果如下

Int(0)
Int(5)
~Int(5)
~Int(0)
6

析构函数调用了,但是仍然输出了正确结果。C++对象的构造析构均是两段式,申请内存+构造对象 & 析构对象+释放内存,但这里看似只执行了第一步。

另一个案例

搜索原因的过程中找到的帖子 C local variable reused

#include
void t();
int main(void){
    t();
    t();
    t();
    return 0;
}

void t(){
    int i;
    i++;
    printf("%d\n",i);
}

输出结果依次是1,2,3,这里是未初始化局部变量i。产生这种结果的原因是,函数栈帧每次被回收时,并没有实际清除所在的地址,导致下次调用该函数,使用的还是原来的栈帧地址。
但是这份代码的结果是未定义的行为,通俗点讲就是不能依赖这种小聪明,该用static就用static。或者为了避免这种未定义行为,必须对局部变量初始化int i = 0;C99/C++11标准不禁未禁止局部变量未初始化就使用的行为,还会自动初始化未初始化的int(为0)。PS: 对于函数体外的变量(即BSS:未初始化数据段)会自动初始化为0。

检测和防止

一种检测方法是编译时加上-Wall选项会提示使用未初始化的变量。

$ g++ test.cc -std=c++11 -Wall
test.cc: In function ‘int main()’:
test.cc:23:32: warning: ‘i’ is used uninitialized in this function [-Wuninitialized]
     printf("%d %d\n", i, f1(0));

再回过头看之前那个使用int包装类Integer的版本,在C++中由于对象每次构造都会调用构造函数来初始化各成员变量,所以不存在重用旧值的问题。
但是问题是在访问这样的本应回收的内存时,编译器不会报错。这点也是C/C++不同于大多数高级语言的地方,这种陷阱需要十分小心。

你可能感兴趣的:(C程序的局部变量被重用现象)