2017-02-28 32 views
2

下面的简单C程序输出42令我惊讶。我期望它输出垃圾值或0,因为堆栈帧foo()bar()不同。输出如何确定为42调用堆栈在2个函数调用之间重复使用

#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

+6

这是** **不确定性。您调用_undefined behaviour_。该代码可以格式化您的硬盘。 **您期望哪个**“垃圾值”? – Olaf

+0

垃圾值恰好在'foo()'堆栈中的位置,其中将存储变量'a'。请参阅编辑的问题。当我优化编译器时,它会打印垃圾。 –

+0

你的C书应该包含一个关于未定义行为的警告。 **阅读**。所以当我读你的评论时,除**'42'之外的任何值都是垃圾。撇开该程序不需要打印任何东西。 – Olaf

回答

1

函数调用堆栈去(其激活记录),在此情况下,两个函数foo和bar是相同的参数,返回类型方面和每个函数中的局部变量。所以第一次调用会把一些东西放在堆栈上,一旦它的激活记录被弹出但没有清除(编译器不会生成清理代码的代码)。现在第二个函数调用基本上结束了使用相同的内存位置,因此使用相同的内存区域从以前的调用获取值。没有在函数中初始化的值被认为是未定义的。在foo和bar情况下,由于两个函数的类似签名以及变量的相同类型和位置,我们得到了相同的值。尝试在一个函数中添加两个整数或整数和一个浮点数,并在下一个逆序排列,您将看到效果。当然,当你调用优化器的时候,它可能会把东西放在注册表中,因此当编译器没有看到foo的优化范围时,你可能会得到未定义的值。

2

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

在组的每个函数,开始类似这种顺序的功能

  1. 参数
  2. 功能的
  3. 返回值
  4. EBP(存储前一帧指针)
  5. 异常处理帧
  6. 局部变量
  7. 缓冲
  8. 被调用者保存寄存器

在上例中,调用main()并按照上述过程。

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

然后它遇到foo()跟在bar()有内存位置相同1,2,3,4步骤。并且您声明了一个名为a的局部变量,它将指向保存局部变量a=42的相同内存位置bar()。所以它在打印时会给出相同的值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