【发布时间】:2011-04-08 01:56:38
【问题描述】:
我只能想象 1)参数; 2) 局部变量;
还有什么?
1) 函数返回地址? 2) 函数名?
【问题讨论】:
-
在 Windows 上它也依赖于calling convention
标签: c++ c compiler-construction programming-languages callstack
我只能想象 1)参数; 2) 局部变量;
还有什么?
1) 函数返回地址? 2) 函数名?
【问题讨论】:
标签: c++ c compiler-construction programming-languages callstack
这确实取决于平台和架构,但通常:
据我所知,函数名称永远不会在堆栈中,除非您的代码将它放在那里。
【讨论】:
我觉得a picture真的是一千个字。
【讨论】:
这取决于调用约定;对于 Unix,您通常在 SYSV ABI(应用程序二进制接口)中查找此信息。
你可能会发现:
返回地址(如果机器是流行的 Intel 架构)。在更现代的架构上,返回地址是在寄存器中传递的。
被调用者保存寄存器——这些寄存器“属于”被调用者选择借用的调用者,因此必须保存和恢复。
任何无法在寄存器中传递的传入参数。在 IA-32 中,no 参数在寄存器中传递;他们都在堆栈上。在 x86-64 中,最多可以在寄存器中传递六个整数和六个浮点参数,因此很少需要为此目的使用堆栈。
您可能会或可能不会找到已保存的堆栈指针或帧指针。大多数现代调用约定都没有帧指针以节省额外的寄存器。在这个设计中,每个帧的大小在编译时就知道了,所以恢复旧的堆栈指针只是添加一个常量的问题。但这使得实现alloca() 变得更加困难。
较旧的 Intel 调用约定同时使用堆栈指针和帧指针,这会消耗一个额外的寄存器,但它简化了 alloca() 并且还简化了堆栈展开。
存储类为auto的局部变量在堆栈上分配。
如果硬件没有提供足够的寄存器来保存所有计算的中间结果,堆栈可能包含编译器临时文件,这些临时文件保存的值会“溢出”。 (如果在任何时候实时中间结果的数量——程序稍后需要的——超过编译器可用于存储中间结果的寄存器数量,就会发生这种情况。)
您可能会发现使用alloca() 分配的变量。
您可能会发现元数据表明哪些 PC 范围在哪些异常处理程序的范围内,或其他非常依赖于平台的异常内容。
C 和 C++ 不支持垃圾回收,但在支持垃圾回收的语言中,您经常会发现元数据,这些元数据标识了您可以在堆栈帧中找到指针的位置。
最后,堆栈可能包含“填充”,用于确保堆栈指针在 8 字节或 16 字节边界上对齐。
调用约定是复杂的野兽,堆栈框架布局不适合胆小的人!
【讨论】: