3

在一篇关于 _cdecl 调用约定的文章中,作者提到:

释放本地存储 当函数分配本地临时空间时,它通过从堆栈点减少所需的空间量来实现,并且必须反转该过程以回收该空间。它通常是通过将之前减去的相同数量添加到堆栈指针来完成的,尽管一系列 POP 指令可以实现相同的效果。

我的问题是:我是否可以简单地将 ESP 设置为当前的 EBP 值,而不是“将减去的相同数量添加到堆栈指针”或“一系列 POP 指令”?

喜欢:

mov esp, ebp

对我来说似乎是一个更好的方法,因为如果我以后更改此函数的局部变量的数量,我就不必费心事后增加值。

4

2 回答 2

4

从技术上讲,这将是对堆栈帧的滥用,它可以捕获堆栈不平衡引起的错误,但这是完全合法的。

不过有几点需要注意,如果你的堆栈空间非常有限,由于大分配或嵌入式设备,每次调用后清理堆栈是值得的。它也使调试更容易一些,因为这样你就知道某些东西使用了错误数量的 args。

此外,如果有人要维护您的代码,他们会发现它非常混乱。

于 2012-02-28T08:09:41.007 回答
2

实际上,这正是该leave指令的作用,它是为了支持高级语言而引入的。但是,它并不经常使用。大多数编译器只是做显式mov esp, ebp; pop ebp序列。另请参阅此问题

但是,有时您可以进行“省略帧指针”优化。这释放了 EBP 以用作通用寄存器,但您(或编译器)随后必须在整个函数期间跟踪 ESP 更改,并使用可能更改的偏移量来处理局部变量或传入参数。如果这样做,则必须使用 pops 或显式 add 将 ESP 恢复为其原始值,然后再返回。

请注意,以上涉及整个功能(即序言/结语);当您需要在函数中间调用特定__cdecl函数时,您不能只将 ESP 恢复为 EBP 值,因为该值仅在函数开始时有效,在为局部变量分配任何空间之前。这里有两种方法:

1)推送参数,调用后恢复ESP:

push offset msg
call _printf
pop ecx ; clobbers ECX but shorter than add esp, 4

2) 将参数移动到保留的堆栈槽;在这种情况下,您不需要恢复 ESP:

mov dword ptr [esp+0], offset msg
call _printf
; no need to change ESP

如果选择第二个选项,则需要确保不在这些插槽中存储任何局部变量。此外,此类mov指令通常比推送要长得多,因此如果代码大小是一个问题,您可能需要考虑这一点。

于 2012-02-28T12:08:20.700 回答