堆栈分配,为什么额外的空间?

spe*_*tre 4 x86 assembly stack gcc memory-alignment

我正在玩一下以更好地掌握调用约定以及如何处理堆栈,但我无法弄清楚为什么main在设置堆栈时会分配三个额外的双字(at <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.
Run Code Online (Sandbox Code Playgroud)

编辑:当然我应该发布C代码:

int func(int a, int b) {
    int c = 9;
    return a + b + c;
}

void main() {
    int x;
    x = func(3, 7);
}
Run Code Online (Sandbox Code Playgroud)

该平台是Arch Linux i686.

Jer*_*fin 5

main当您输入函数时,函数的参数(包括但不限于)已经在堆栈中.您在函数内分配的空间用于局部变量.对于具有简单返回类型的函数,例如int,返回值通常位于寄存器中(eax在x86上使用典型的32位编译器).

例如,如果main是这样的话:

int main(int argc, char **argv) { 
   char a[35];

   return 0;
}
Run Code Online (Sandbox Code Playgroud)

...我们希望在堆栈中分配至少35个字节,因为我们输入main来腾出空间a.假设一个32位实现,通常会四舍五入到4的下一个倍数(在这种情况下为36),以保持堆栈的32位对齐.我们不希望看到为返回值分配任何空间.argc并且argv会在堆栈中,但是在main进入之前它们已经在堆栈中,因此main不必为它们分配空间.

在上面的例子中,在分配空间之后a,a将典型地开始于[esp-36],argv将处于[esp-44]并且argc将处于[esp-48](或者这两个可能是相反的 - 取决于参数是从左到右还是从右到左推).如果您想知道我为什么跳过[esp-40],那将是返回地址.

编辑:这是进入函数时堆栈的图表,并在设置堆栈帧之后:

在此输入图像描述

编辑2:根据您更新的问题,您所拥有的内容略微迂回,但并不是特别难以理解.在进入时main,它不仅为本地变量分配空间main,而且还为您传递给调用函数的参数分配空间main.

这至少占了分配的一些额外空间(尽管不一定全部).