5
#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);
    }
}

为什么是这样?我有什么误解?

4

2 回答 2

7

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% 表明您的程序存在导致未定义行为的某些东西。您在编译器中遇到错误的可能性非常非常小。除了这两个原因,我想不出还有什么可能是原因。

于 2019-05-28T19:49:04.817 回答
5

实际上,当堆栈确实存在时(正如 Broman 的回答所指出的,尽管需要支持递归,但不需要堆栈存在编译器通常会生成仅在函数中调整堆栈指针一次的代码条目,并在函数退出时再次出现,即使函数中有限制单个变量生命周期的子范围。

如果您习惯于手工编写汇编语言,这可能看起来很奇怪。最基本的原因是,这意味着存在于堆栈中的每个变量都有一个“堆栈槽”,在整个函数中具有固定位置,这为编译器提供了最大的灵活性来移动机器指令以进行优化。

于 2019-05-28T21:07:22.800 回答