如果变量离开作用域,为什么C不会递减堆栈指针?

时间:2019-05-28 19:32:50

标签: c stack-pointer

#include <stdio.h>
void main() {
    {
        int x;
        printf("%p\n", &x);
    }
    {
        int x;
        printf("%p\n", &x);
    }
}

我认为运行此命令将两次输出相同的内容。当它声明第一个变量时,它会增加堆栈指针,但会离开作用域,因此会减小它,然后第二次重复该过程,因此int x将在堆栈上占用相同的内存位置时间。

但事实并非如此。堆栈指针不会递减,并且两种情况下的int x都在堆栈中占据不同的位置。实际上,即使第一个int x的范围已消失,仍然可以访问。

#include <stdio.h>
void main() {
    {
        int x = 10;
        printf("%p\n", &x);
    }
    {
        int x = 25;
        printf("%p\n", &x);
    }
    {
        int x = 71;
        printf("%p\n", &x);

        int *p = &x;
        printf("%i %i %i\n", *(p + 2), *(p + 1), *p);
    }
}

这是为什么?我有什么误会?

2 个答案:

答案 0 :(得分:6)

C标准甚至没有提到堆栈。不需要变量时,编译器可以自由地优化变量。 C标准中绝对没有任何内容暗示打印输出不应该相等或不相等。

在我的计算机上,这通过根据优化级别提供不同的输出来体现出来:

$ gcc c.c

/tmp$ ./a.out 
0x7ffd8733c3ac
0x7ffd8733c3a8

/tmp$ gcc c.c -O3

/tmp$ ./a.out 
0x7fff4e91544c
0x7fff4e91544c
  

事实上,即使第一个“ int x”的作用域消失了,仍然可以访问。

访问超出范围的变量会导致不确定的行为,这意味着任何事情都可能发生。这包括程序按预期运行的情况。

这是第二个代码段的输出,具有不同的优化:

/tmp$ ./a.out 
0x7ffd4df94864
0x7ffd4df94860
0x7ffd4df9485c
10 25 71

/tmp$ gcc c.c -O3

/tmp$ ./a.out 
0x7ffc30b4e44c
0x7ffc30b4e44c
0x7ffc30b4e44c
0 0 71

当您根据优化级别获得不同的行为时,几乎有100%的迹象表明您的程序中有某些东西会导致未定义的行为。您在编译器中遇到错误的机会非常小。除了这两个原因外,我想不出其他任何可能的原因。

答案 1 :(得分:4)

实际上,当堆栈确实存在时(正如Broman的答案所指出的,虽然有 要求支持递归,但是并不需要堆栈存在)编译器通常会生成即使在函数中存在限制单个变量生存期的子作用域,该代码也只能在函数进入时一次调整堆栈指针,并在函数退出时再次调整堆栈指针。

如果您习惯手工编写汇编语言,这似乎很奇怪。造成这种情况的最基本原因是,这意味着驻留在堆栈上的每个变量都有一个“堆栈插槽”,在整个函数中都有一个固定位置,这为编译器在移动机器指令时提供了最大的灵活性。进行优化。