为什么函数参数在 x86 上至少占用 4 个字节的堆栈?

Why function parameter occupy at least 4 bytes stack on x86?

如果函数参数在 x86 上的堆栈中分配,则通过 push/pop 分配至少 4 个字节。如果每个函数调用有许多小于 4 字节的参数,这会浪费内存。一个原因可能是push and pop work on 4 bytes least,但为什么不直接在esp上操作以节省堆栈space,这样可以将1字节中的4个参数打包到一个4字节内存中,如下所示?

sub esp, 4
mov byte ptr [esp], para1
mov byte ptr [esp+1], para2
mov byte ptr [esp+2], para3
mov byte ptr [esp+3], para4
call func

此类行为通常由应用程序二进制接口 (ABI) 控制,最常用的 x86 ABI(Win32 和 Sys V)仅要求每个参数至少占用 4 个字节。这主要是因为如果数据没有正确对齐,大多数 x86 实现都会遭受性能损失。虽然您的示例不会 "de-align" 堆栈,但仅采用三个字节大小的参数的子例程会这样做。当然,可以在 ABI 中定义特殊规则来克服这一点,但这会使事情变得复杂而收效甚微。

还请记住,x86 ABI 是在 1990 年左右设计的。当时,指令数是衡量某段代码速度的一个很好的衡量标准。如果 para1-para4 位于寄存器中,则您的示例需要一条额外的指令而不是四次推送,而在最坏的情况下需要五条额外的指令,所有参数都必须从内存中加载(x86 支持直接推送内存位置)。

此外,在您的示例中,您用在堆栈上节省 12 个字节来换取 14 个额外的代码字节:您的代码序列需要 18 个字节的代码,以防 para1-para4(例如 al-dl)位于寄存器中,而四个推送需要 4 个字节。所以总的来说,只有当你的代码中有递归时,你才能减少内存占用。

一个更一般的答案是平台的堆栈字长通常是指向内存位置的指针的宽度。由于您正在处理 32 位应用程序,因此预计 32 位字大小并且将是堆栈对齐。