我玩了一会儿,以更好地掌握调用约定和堆栈的处理方式,但我无法弄清楚为什么 main 在设置堆栈时分配了三个额外的双字(在 <main+0>
处)。它既不是 8 字节也不是 16 字节对齐,所以据我所知,这不是原因。正如我所见, main 需要 12 个字节来表示 func 和返回值的两个参数。
我错过了什么?
该程序是在 x86 架构上使用“gcc -ggdb”编译的 C 代码。
编辑:我从 gcc 中删除了 -O0 标志,它对输出没有影响。
(gdb) disas main
Dump of assembler code for function main:
0x080483d1 <+0>: sub esp,0x18
0x080483d4 <+3>: mov DWORD PTR [esp+0x4],0x7
0x080483dc <+11>: mov DWORD PTR [esp],0x3
0x080483e3 <+18>: call 0x80483b4 <func>
0x080483e8 <+23>: mov DWORD PTR [esp+0x14],eax
0x080483ec <+27>: add esp,0x18
0x080483ef <+30>: ret
End of assembler dump.
编辑:当然我应该发布 C 代码:
int func(int a, int b) {
int c = 9;
return a + b + c;
}
void main() {
int x;
x = func(3, 7);
}
平台是 Arch Linux i686。
最佳答案
当您进入函数时,函数的参数(包括但不限于 main
)已经在堆栈中。您在函数内部分配的空间用于局部变量。对于具有简单返回类型的函数,例如 int
,返回值通常在寄存器中( eax
,在 x86 上使用典型的 32 位编译器)。
例如,如果 main
是这样的:
int main(int argc, char **argv) {
char a[35];
return 0;
}
...当我们进入 main 为
a
腾出空间时,我们希望看到堆栈上至少分配了 35 个字节。 .假设是 32 位实现,通常会向上取整为 4 的下一个倍数(在本例中为 36)以保持堆栈的 32 位对齐。我们不希望看到为返回值分配的任何空间。 argc
和 argv
将在堆栈上,但它们在 main
之前已经在堆栈上已输入,所以 main
不需要做任何事情来为他们分配空间。在上述情况下,在为
a
分配空间后, a
通常从 [esp-36]
开始, argv
将在 [esp-44]
和 argc
将在 [esp-48]
(或者这两个可能会颠倒——取决于参数是从左到右还是从右到左)。如果你想知道我为什么跳过 [esp-40]
,那将是返回地址。编辑:这是函数入口处和设置堆栈框架后的堆栈图:
编辑 2:根据您更新的问题,您所拥有的有点迂回,但并不是特别难以理解。进入
main
,它不仅为 main
的局部变量分配空间,还有你传递给你从 main
调用的函数的参数.这至少占了分配的一些额外空间(尽管不一定是全部)。
关于gcc - 栈分配,为什么要多出空间?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9862017/