美文网首页
C程序的局部变量被重用现象

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

作者: 哈莉_奎茵 | 来源:发表于2018-03-27 06:46 被阅读0次

    原始问题

    起源于《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<stdio.h>
    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程序的局部变量被重用现象

          本文链接:https://www.haomeiwen.com/subject/bdxjcftx.html