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

Posted

技术标签:

【中文标题】为啥函数参数在 x86 上占用至少 4 个字节的堆栈?【英文标题】:Why function parameter occupy at least 4 bytes stack on x86?为什么函数参数在 x86 上占用至少 4 个字节的堆栈? 【发布时间】:2021-06-06 23:43:43 【问题描述】:

如果函数参数在 x86 上的堆栈中分配,则通过 push/pop 分配至少 4 个字节。如果每个函数调用有许多小于 4 字节的参数,这会浪费内存。一个原因可能是push and pop work on 4 bytes least,但为什么不直接对esp 进行操作以节省堆栈空间,该堆栈空间可以将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

【问题讨论】:

在汇编中,没有人阻止你这样做。 可以在任何模式(16、32 或 64 位)下执行 2 字节 push ax,它通常在 16 位模式之外无用.正如您所说,正常的调用约定填充堆栈参数以填充整个传递参数的“槽”(一个寄存器,或堆栈内存的寄存器宽度块)。 最近的副本有一些其他类似的答案:Why argument's size of function is increased to word size? 【参考方案1】:

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

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

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

【讨论】:

【参考方案2】:

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

【讨论】:

以上是关于为啥函数参数在 x86 上占用至少 4 个字节的堆栈?的主要内容,如果未能解决你的问题,请参考以下文章

为啥布尔值比字符消耗更多的内存?

为啥c#中bool要占4个字节 32位呢 为啥不用像byte 1个字节存储呢

x86上为啥C语言调用一个函数要先把参数压栈,之后才是返回地址

C/C++重点知识点

用指针作为参数传值是否更节省内存?(c/c++/golang)

为啥C语言中bool型变量占用一个字节