来自http://en.wikipedia.org/wiki/Stack_pointer#Structure
我想知道为什么函数的返回地址放在该函数的参数之上?
在 Drawline参数之前将返回地址压入堆栈更有意义,因为当返回地址<时,不再需要参数弹出/ strong>以返回调用函数。
更喜欢上图所示实施的原因是什么?
答案 0 :(得分:6)
返回地址通常是通过call
机器命令[在本地语言的instruction set]中推送的,而参数和变量是用多个机器命令推送的 - 编译器创建这些命令。 p>
因此,返回地址是调用者推送的最后一个东西,并且在被调用者推送任何[局部变量]之前。
参数全部在返回地址之前被推送,因为跳转到实际函数并将返回地址插入堆栈是在同一台机器命令中完成的。
另外,另一个原因是 - 调用者是在堆栈上为参数分配空间的人 - 它[调用者]也应该是清理它的人。
答案 1 :(得分:1)
原因很简单:函数参数被调用函数推送到堆栈上(这是唯一可以执行此操作的函数,因为只有它具有必要的信息;毕竟,这样做的全部要点是传递信息到被叫函数)。返回地址由函数调用机制推送到堆栈。在调用函数设置参数之后,该函数被称为,因为在调用之后它被执行的被调用函数,而不是调用函数。
好的,现在你可以争辩说调用函数可以将参数放在当前使用的堆栈之外,然后被调用的函数可以相应地调整堆栈指针。但这样做不会很好,因为在任何时候都可能存在中断或信号,这会将当前状态推送到堆栈以便以后恢复(如果任务切换这样做,我不会感到惊讶,太)。但是如果你设置超出当前堆栈的参数,那些异步事件将覆盖它,并且由于你无法预测它们何时会发生,你无法避免(除了禁用,这可能有其他缺点,甚至是不可能的)任务开关)。基本上,超出当前堆栈的所有内容都必须被认为是不稳定的。
另请注意,这与谁清理参数的问题无关。原则上,被调用函数可以调用参数的调用析构函数,即使它们位于调用者的堆栈帧中。此外,许多处理器(包括x86)都有指令在返回时自动在返回地址之上弹出额外的空间(例如,Pascal编译器通常会这样做,因为在Pascal中你不会在返回内存之外进行任何清理,至少从当时的处理器来看,使用该处理器指令清理效率更高(我不知道现代处理器是否仍然如此)。但由于可变长度参数列表,C没有使用该机制:对于那些,该机制不适用,因为您需要在编译时知道要释放多少额外空间,并且K&amp; RC不需要前向声明可变参数函数(C89可以,但很少)如果任何编译器利用这一点,由于与旧代码的兼容性),那么调用函数无法知道是否要清理参数,除非它必须始终这样做。