【问题标题】:Why are function parameters pushed earlier on call stack than the return address?为什么函数参数在调用堆栈上比返回地址更早推送?
【发布时间】:2012-03-04 03:41:22
【问题描述】:

来自http://en.wikipedia.org/wiki/Stack_pointer#Structure

我想知道为什么函数的返回地址放在该函数的参数之上?

返回地址绘图线参数之前压入堆栈更有意义,因为当返回地址 被弹出以返回调用函数。

首选上图所示实现的原因是什么?

【问题讨论】:

  • 请注意,这种安排不是 C++ 标准的一部分。它特定于单个 CPU、编译器和/或操作环境。那篇文章中描述的情况是典型的,对于 GCC/X86 是准确的,但绝不是普遍的。
  • 参数和本地的数量可能不固定。如果返回地址不在中间,如何确定所有 3 个地址?
  • @Pubby:我没有答案,但这种情况也发生在 Locals of DrawSquareParameters of DrawLine 上。跨度>
  • 这只是自然的执行顺序。调用者首先推送参数,然后是 CALL,然后函数为本地变量调整堆栈指针。以任何其他方式做都会效率低下。

标签: c++ function gcc callstack


【解决方案1】:

返回地址通常通过call 机器命令推送,[在本机语言中为instruction set],而参数和变量通过编译器创建的多个机器命令推送。

因此,返回地址是调用者推送的最后一件事,在被调用者推送的任何[局部变量]之前。

参数都压在返回地址之前,因为跳转到实际函数和将返回地址插入堆栈是在同一个机器命令中完成的

另外,另一个原因是 - 调用者是在堆栈上为参数分配空间的人 - 它[调用者] 也应该是清理它的人。

【讨论】:

  • “它 [调用者] 也应该是清理它的人。” - 实际上,在几乎所有不支持可变参数的调用约定中(例如stdcall),清理责任都取决于被调用者以减少可执行文件大小(清理代码只在函数末尾编写一次,而不是每个函数调用一次)。
【解决方案2】:

原因很简单:函数参数被调用函数压入堆栈(这是唯一可以这样做的,因为只有它具有必要的信息;毕竟这样做的全部意义在于传递被调用函数的信息)。通过函数调用机制将返回地址压入堆栈。 调用函数设置了参数之后调用函数,因为调用之后执行的是被调用函数,而不是调用函数。

好的,所以现在你可以争辩说调用函数可以将参数超出当前使用的堆栈,然后被调用的函数可以相应地调整堆栈指针。但这不会很好,因为在任何时候都可能存在中断或信号,这会将当前状态推入堆栈以便稍后恢复(如果任务切换也这样做,我不会感到惊讶) .但是如果你将参数设置在当前堆栈之外,那些异步事件会覆盖它,并且由于你无法预测它们何时会发生,你无法避免这种情况(除了禁用,这可能还有其他缺点甚至是不可能的,在这种情况下任务切换)。基本上,当前堆栈之外的所有内容都必须被视为 volatile。

还要注意,这与谁清理参数的问题无关。原则上,被调用函数可以调用参数的调用析构函数,即使它们在物理上位于调用者的堆栈框架中。此外,许多处理器(包括 x86)具有在返回时自动在返回地址上方弹出额外空间的指令(例如,Pascal 编译器通常会这样做,因为在 Pascal 中,除了返回内存之外,您没有任何清理,并且至少在当时的处理器,使用该处理器指令进行清理效率更高(我不知道现代处理器是否仍然如此)。但是由于可变长度的参数列表,C没有使用该机制:对于那些,该机制不适用,因为您需要在编译时知道要释放多少额外空间,并且 K&R C 不需要前向声明可变参数函数(C89 需要,但很少有编译器利用这一点,因为为了与旧代码兼容),因此调用函数无法知道是否清理参数,除非它必须始终这样做。

【讨论】:

    猜你喜欢
    • 2016-12-11
    • 1970-01-01
    • 2020-11-22
    • 2013-07-25
    • 1970-01-01
    • 2011-07-08
    • 2013-03-07
    • 1970-01-01
    • 2020-04-19
    相关资源
    最近更新 更多