了解 C/C++ 中函数调用的堆栈框架? [关闭]

Posted

技术标签:

【中文标题】了解 C/C++ 中函数调用的堆栈框架? [关闭]【英文标题】:Understanding stack frame of function call in C/C++? [closed] 【发布时间】:2013-04-11 20:50:12 【问题描述】:

我也是 C/C++ 和汇编语言的新手。 这也可能是非常基本的问题。 我试图了解堆栈帧是如何构建的以及哪些变量(参数)以什么顺序被推送到堆栈? 一些搜索结果表明...... C/C++ 的编译器根据函数内执行的操作来决定。例如,如果假设函数只是将传递的 int 参数的值增加 1 并返回(类似于 ++ 运算符),它将把函数的所有参数和局部变量放在寄存器中的函数中并执行加法。 ...想知道哪个寄存器用于返回/按值传递?....如何返回引用? .....b/w eax、ebx、ecx 和 edx 的区别。

请求书籍/博客/链接或任何类型的材料来理解寄存器、堆栈和堆引用在函数调用期间被使用/构建和销毁....以及主函数是如何存储的? 提前致谢

【问题讨论】:

C++ 语言对此只字未提。编译器可以做他们喜欢的事情。因此,您的问题应该具体说明您正在谈论哪些编译器,因为答案将取决于此。你最好的理解可能是编写一些 C++ 代码,看看你最喜欢的编译器实际上做了什么。 里程太宽,不适合 SO。 您对此有什么了解。您是计算机科学家还是软件开发人员?你的背景是什么? 问题也被问到程序员:programmers.stackexchange.com/questions/195385/… 【参考方案1】:

你的问题在这里是临界的。 programmers 可能是一个更好的地方。

理解堆栈等概念的好书可能是 Queinnec 的Lisp In Small Pieces(它很好地解释了 Lisp 的堆栈是什么)。另外,SICP 对read来说是一本好书。

D.Knuth 和MMIX 的书也不错。

仔细阅读***Call stack页面。

理论上,不需要调用堆栈,并且一些语言和实现(例如旧的 SML/NJ)不使用任何堆栈(而是在垃圾收集堆中分配调用帧)。请参阅 A.Appel 的旧论文 Garbage Collection Can be Faster than Stack Allocation(并了解更多关于 garbage collection 的一般信息)。

通常 C 和 C++ 实现都有一个堆栈(并且经常使用硬件堆栈)。一些 C 局部变量可能没有任何堆栈位置(因为它们已经过优化,或者保存在寄存器中)。有时,C 局部变量的堆栈位置可能会改变(编译器会在某些情况下使用一个调用堆栈槽,而对同一局部变量的其他情况使用另一个调用堆栈槽)。当然,一些临时值可能会像您的局部变量一样编译(所以留在寄存器中,在一个堆栈槽中然后在另一个堆栈槽中,等等......)。当优化编译器时,可能会用变量做一些奇怪的技巧。

在一些老机器IBM/360或IBM z/series上,没有硬件栈; C 编译器使用的堆栈是一种软件约定(例如,某些寄存器专用于该用途,没有特定的硬件支持)

想想递归定义的函数的执行(或解释)(就像古老的 factorial 天真编码)。了解recursion(一般情况下,in computer science)、primitive recursive functions、lambda calculus、denotational semantics、stack automaton、register allocation、tail calls、continuations、ABI2@3 Posix signals、sigaltstack(2)、getcontext(2)、longjmp(3)etc....等......

阅读有关Computer Architecture 的书籍。在实践中,调用堆栈非常重要,以至于几个硬件资源(包括 堆栈指针 寄存器,通常是调用帧 基指针 寄存器,可能还有隐藏的机器,例如缓存相关)专用于通用处理器上。

您还可以查看 GCC 编译器使用的中间表示。然后使用-fdump-tree-all 或GCC MELT probe。如果查看生成的程序集,请务必将-S -fverbose-asm 传递给您的gcc 命令。

另请参阅linux assembly howto。

我给了很多链接。很难更好地回答,因为我不知道你的背景。

【讨论】:

【参考方案2】:

我正在尝试了解堆栈帧是如何构建的以及哪些 变量(参数)按什么顺序入栈?

这取决于处理器的架构。但是,通常,堆栈从高地址向低地址增长(如果我们将内存地址视为数值)。一个堆栈帧是“这个函数放在堆栈上的任何东西”

放入堆栈的“东西”通常是:

将地址返回给调用函数。 帧指针,指向调用开始时的堆栈帧。 在此函数返回时需要“保留”的已保存寄存器。 局部变量。 调用堆栈中“next”函数的参数。

C/C++ 的编译器根据在一个 功能。例如,如果函数假设只是增加值 通过 1 传递的 int 参数并返回(类似于 ++ 运算符)它 会把所有... 函数的参数和函数内的局部变量在寄存器中 并执行加法......想知道哪个寄存器用于 返回/按值传递?....引用是如何返回的?

编译器对如何传递参数有规则,对于常规函数调用[即不是“内联”函数],参数总是以相同的顺序传递,在寄存器和堆栈内存的相同组合中。如果不是这种情况,编译器在决定传递参数之前必须确切地知道函数做了什么。

不同的处理器架构有不同的规则。 x86-32 通常有一个或两个寄存器用于输入参数,通常一个寄存器用于返回值。 x86-64 最多使用 5 个寄存器将前五个值传递给函数。任何进一步的参数都在寄存器中传递。

返回一个引用与返回任何其他值没有什么不同。值(在这种情况下是返回对象的地址)。在 x86-32 中,返回值在 EAX 中。在 x86-64 中,返回值在 RAX 中。在 ARM 中,R0 用于返回值。在 29K 中,R96 用于返回值。

【讨论】:

以上是关于了解 C/C++ 中函数调用的堆栈框架? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

lua C API

c语言的堆栈是怎么回事!!

Lua封装&C++实践—— C++调用Lua函数的封装

Lua封装&C++实践—— C++调用Lua函数的封装

Lua封装&C++实践—— C++调用Lua函数的封装

C/C++源代码可视化? [关闭]