调用堆栈在两个函数调用之间重用

时间:2017-02-28 00:45:10

标签: c

简单的C程序输出42令我惊讶。我希望它输出一个垃圾值或0,因为foo()bar()的堆栈帧不同。输出如何确定为42

#include <stdio.h>
void foo(void){
    int a;
    printf("%d\n",a);
}

void bar(){
    int a=42;
}

int main(){
    bar();
    foo();
    return 0;
}

>>gcc -o test test.c

>>./test

42

当我指示编译器优化代码时,它会打印垃圾!

>>gcc -O -o test test.c

>>./test

2487239847

2 个答案:

答案 0 :(得分:2)

是的,值42是垃圾。以下是对它的解释:

堆栈中的每个函数都与此顺序类似

  1. 功能的参数
  2. 函数的返回值
  3. EBP(存储前一帧指针)
  4. 异常处理程序框架
  5. 本地变量
  6. 缓冲
  7. callee save register
  8. 在上面的示例中,调用main()并遵循上述过程。

    然后遇到bar()后面提到的1,2,3,4步骤然后将局部变量a=42存储在内存中(5)然后6,7跟随它然后它会出来记忆。

    然后它遇到foo()跟随1,2,3,4步骤在bar()的内存位置相同。并且您声明了一个名为a的局部变量,它将指向bar()持有局部变量a=42的相同内存位置。所以它在打印时给出相同的值42,这实际上是一个垃圾值。

    要验证这一点,请尝试以下示例:打印7

    #include <stdio.h>
    #include <string.h>
    
    void foo() {
        int b;
        printf("%d\n",b);
    }
    
    void zoo() {
      int dummy = 7;
    }
    void bar(){
        int a1=3;
    }
    
    int main(){
        bar();
        zoo();
        foo();
        return 0;
    }
    

    参考:Doc

答案 1 :(得分:1)

函数调用继续堆栈(它们的激活记录),在这种情况下,两个函数foo和bar在参数,返回类型和每个函数内的局部变量方面是相同的。因此,第一次调用会在堆栈上放置一些内容,一旦完成,它的激活记录就会被弹出但不会被清除(编译器不会生成代码来清理它们)。现在,第二个函数调用本质上最终使用相同的内存位置,因此使用相同的内存区域从前一个调用获取值。未在函数中初始化的值被视为未定义。在foo和bar的情况下,由于两个函数的类似签名以及变量的相同类型和位置,我们得到了相同的值。尝试在一个函数中添加两个整数或整数和一个浮点数,然后在下一个中反转顺序,您将看到效果。当然,当你调用优化器时,它可能会把东西放在寄存器上,因此当编译器看不到优化的余地时,你可能得到未定义的值,就像foo一样。