关于C中堆栈分配的问题

时间:2016-02-25 08:53:28

标签: c memory-management stack

以下代码段有意访问t[4]之后的下一个sizeof(int)字节,因此我知道此处正在进行的错误。我只是作为一个实验来看看编译器如何处理堆栈分配。

int t[5], i;

for (i = 0; i <= 5; i++) {
   t[i] = 0;
}

在Windows上执行此代码时,使用移植版本的GNU C编译器,程序总是陷入无限循环。我确信这只会发生,因为ti依次在堆栈上按顺序分配,而t[5]指向与i变量相同的地址。因此,在执行t[5] = 0时,程序实际上将i的值设置为零。

但是,当使用不同版本的GNU C编译器编译时,我永远不会得到无限循环。 t[5]的地址与i的地址不同。

我的问题是,为什么这种不同的行为?我知道你不应该假设这个结果,但不是堆栈分配应该以同样的方式发生吗?

我真正好奇的是编译器如何管理这些堆栈分配。有填充物吗?订单总是与源代码中的相同吗?显然,这与C标准无关,实现之间存在差异,甚至同一编译器的不同版本也存在差异。我很好奇,虽然在这种特殊情况下可能的结果和考虑因素是什么。

3 个答案:

答案 0 :(得分:9)

您正在处理未定义的行为。编译器不需要按顺序布局自动变量(因为它们出现在源代码中)。其中一些可能在寄存器中,或者它们可能以不同的方式订购,例如,如果较小的偏移更便宜。

这样的要求仅存在于结构的成员中(成员之间具有任意填充)。

  

有填充物吗?

是的,编译器会遵守每种类型的对齐要求并相应地放置变量。

  

订单总是与源代码中的相同吗?

不,但这是许多漏洞依赖的东西。缓冲区溢出可能会覆盖相邻变量并影响整个程序的执行。

答案 1 :(得分:4)

另一个人说为什么从标准的角度来看这个行为,我会说你的代码编译优化和执行后可能会发生什么。

第一次:循环可能会展开并执行6次:

t[0] = 0;
t[1] = 0;
t[2] = 0;
t[3] = 0;
t[4] = 0;
t[5] = 0;
i = 6;

允许优化,这是可能发生的事情。更多:如果以后没有使用i,则可以将其全部删除。

第二:编译器可能会将i保留在寄存器中而不进行任何堆栈分配。

第三:它可能会在堆栈上以任何顺序放置变量。对于将变量保存在内存中(以及它们所在的位置)的顺序没有实际要求。

如何知道发生了什么?查看生成的程序集。这是了解情况发生的唯一方法。

顺便说一句:无限循环并不总是发生在Windows上。事实上,我无法逐字强制你的代码成为一个无限循环。

答案 2 :(得分:3)

访问t[5]是未定义的行为。最后一项是t[4]t[0],t[1],t[2],t[3],t[4]),没有t[5]

通过未定义的行为,任何事情都可能发生。它可能会给出预期的结果或完全混乱。

  

我的问题是,为什么会出现这种不同的行为?

正如我之前写的那样,UB你不能指望任何事情。如果你多次尝试,你甚至可以在同一台机器上得到另一个结果。