作为扩展我的类(class)的一部分,我正在慢慢地沿着编程抽象的阶梯下降。现在我已经很好地掌握了 C,并且正在准备编写一些汇编(特别是 ARM 汇编)。
我遇到过调用约定的主题,虽然我通常理解它们的含义,但似乎从未被提出或回答的问题是:
为什么被调用者不能处理堆栈上的变量参数?
到处都说被调用的函数不知道传递了多少个参数,但在这种情况下,为什么不能简单地将数据放入寄存器或将其插入堆栈顶部以供调用调用的函数要使用吗?
我问这个问题是针对任何利用堆栈进行子例程通信的架构,而不仅仅是 ARM 或 x86。
最佳答案
被调用者可以从堆栈中清除变量参数。事实上,我做过一次。但代码相当大。
无论如何,在cdecl约定中,调用者清理堆栈的主要原因是其他的。 (毕竟变量参数程序很少)
在某些架构上(通常非常小,如旧的 8080 或 6800),没有 ret n 指令来自动执行堆栈清理,并且通常它们无法使用堆栈指针也是如此。
因此,被调用者必须首先从堆栈中弹出返回地址才能到达参数,然后弹出所有参数,然后推回返回地址。对于 3 个参数,它在 stdcall 约定中看起来像这样:
push arg1
push arg2
push arg3
call proc
proc:
pop r1 ; the return address
pop r2
pop r2
pop r2
push r1
ret
当使用cdecl约定时,可以节省2条指令和1个寄存器的使用:
push arg1
push arg2
push arg3
call proc
pop r2
pop r2
pop r2
proc:
ret
因为对于单一语言来说,最好在所有平台上使用单一调用约定,所以 CCALL 更简单、更通用,看起来更好。 (C语言是在6800还是高科技的时代创建的)。
但请注意,在这些平台上,汇编程序和 native 语言(例如不同类型的 BASIC)通常使用寄存器参数传递,这在如此小的系统上当然要快得多。
无论如何,这只是一个传统。您可以将编译器设置为使用您想要的任何约定。例如,WinAPI是用C++编写的,但仍然使用stdcall约定,因为它在x86平台上更好。
关于assembly - 什么阻止被调用者清理堆栈?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19822045/