调用函数时堆栈上有什么?
我只能想象 1)参数; 2)局部变量;
还有什么?
1)函数返回地址? 2)函数名称?
I can only imagine
1) parameters;
2) local variables;
what else?
1) function return address?
2) function name?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
它确实取决于平台和体系结构,但通常是:
有时 -据我所知,
It really depends on platform and architecture, but typically:
Function name is never in the stack, to the best of my knowledge, unless your code places it there.
我认为一张图片确实是一千个单词。
I think that a picture really is a thousand words.
这取决于调用约定;对于 Unix,您通常在 SYSV ABI(应用程序二进制接口)中查找此信息。
您可能会发现:
返回地址(如果机器是流行的 Intel 架构)。在更现代的架构上,返回地址在寄存器中传递。
被调用者保存寄存器——这些寄存器“属于”被调用者选择借用的调用者,因此必须保存和恢复。
任何无法在寄存器中传递的传入参数。在 IA-32 中,没有参数在寄存器中传递;它们都入栈。在 x86-64 中,最多可以在寄存器中传递六个整数和六个浮点参数,因此很少需要为此目的使用堆栈。
您可能会也可能找不到已保存的堆栈指针或帧指针。大多数现代调用约定都没有帧指针,以节省额外的寄存器。在这个设计中,每个帧的大小在编译时就已知,因此恢复旧的堆栈指针只需添加一个常量即可。但这使得实现
alloca()
变得更加困难。较旧的 Intel 调用约定同时使用堆栈指针和帧指针,这会消耗额外的寄存器,但它简化了
alloca()
以及堆栈展开。存储类别为
auto
的局部变量在堆栈上分配。如果硬件没有提供足够的寄存器来保存所有计算的中间结果,堆栈可能包含保存“溢出”值的编译器临时值。 (如果在任何时候,实时中间结果的数量(程序稍后需要的结果)超过编译器可用于存储中间结果的寄存器数量,就会发生这种情况。)
您可能会发现用 < 分配的变量code>alloca()。
您可能会找到元数据,说明哪些 PC 范围在哪些异常处理程序的范围内,或者其他非常依赖于平台的异常内容。
C 和 C++ 不支持垃圾收集,但在支持垃圾收集的语言中,您经常会找到元数据来标识在堆栈帧中的位置找到指针。
最后,堆栈可能包含“填充”,用于确保堆栈指针在 8 字节或 16 字节边界上对齐。
调用约定是复杂的野兽,堆栈框架布局不适合胆小的人!
It depends on the calling convention; for Unix, you typically look up this information in the SYSV ABI (Application Binary Interface).
You may find:
Return address (if the machine is a popular Intel architecture). On more modern architectures, the return address is passed in a register.
Callee-saves registers—these are registers that "belong" to the caller which the callee has chosen to borrow and must therefore save and restore.
Any incoming parameters that could not be passed in registers. In IA-32, no parameters are passed in registers; they all go on the stack. In x86-64, up to six integer and six floating-point parameters can be passed in registers, so it is seldom necessary to use the stack for that purpose.
You may or may not find a saved stack pointer or frame pointer. Most modern calling conventions go without a frame pointer in order to save an extra registers. In this design, the size of each frame is known at compile time, so restoring the old stack pointer is just a matter of adding a constant. But it makes it harder to implement
alloca()
.The older Intel calling conventions use both stack pointer and frame pointer, which burns an extra register, but it simplifies
alloca()
and also stack unwinding.Local variables with storage class
auto
are allocated on the stack.The stack may contain compiler temporaries that hold values which are "spilled" if the hardware does not provide enough registers to hold all the intermediate results of computations. (This happens if at any point the number of live intermediate results—the ones that will be needed later in the program—exceeds the number of registers available to the compiler for storing intermediate results.)
You may find variables allocated with
alloca()
.You may find metadata that says which PC ranges are in scope for which exception handlers, or other very platform-dependent exception stuff.
C and C++ do not support garbage collection, but in a language that does, you will often find metadata that identifies where in the stack frame you will find pointers.
Finally, the stack may contain "padding" used to ensure that the stack pointer is aligned on an 8-byte or 16-byte boundary.
Calling conventions are complex beasts, and stack-frame layout is not for the faint of heart!