我似乎在编程语言设计中得到了调用堆栈的想法。但我找不到(可能,我只是不够努力搜索)对堆栈帧是什么的正确解释。
所以我想请别人用几句话向我解释。
答案 0 :(得分:155)
堆栈帧是一个被推入堆栈的数据帧。在调用堆栈的情况下,堆栈帧将表示函数调用及其参数数据。
如果我没记错的话,首先将函数返回地址压入堆栈,然后将局部变量的参数和空间压入堆栈。它们共同构成了“框架”,尽管这可能取决于架构。处理器知道每帧中有多少字节,并在帧被推出并从堆栈弹出时相应地移动堆栈指针。
更高级别的调用堆栈与处理器的调用堆栈之间存在很大差异。
当我们谈论处理器的调用堆栈时,我们讨论的是在汇编或机器代码中处理字节/字级的地址和值。在讨论高级语言时有“调用堆栈”,但它们是由运行时环境管理的调试/运行时工具,因此您可以记录程序出错的位置(在高级别)。在这个级别,行号,方法和类名之类的东西通常是已知的。当处理器获得代码时,它绝对没有这些东西的概念。
答案 1 :(得分:47)
如果您非常了解堆栈,那么您将了解内存在程序中的工作方式,如果您了解内存在程序中的工作原理,您将了解函数存储在程序中的方式,如果您了解函数存储在程序中的方式,您将理解递归函数的运行方式如果您了解递归函数的工作原理,您将了解编译器的工作原理,如果您了解编译器的工作原理,您的思维将作为编译器工作,您将很容易地调试任何程序
让我解释一下堆栈是如何工作的:
首先你必须知道堆栈中的函数存储:
堆存储动态内存分配值。 堆栈存储自动分配和删除值。
让我们用例子来理解:
def hello(x):
if x==1:
return "op"
else:
u=1
e=12
s=hello(x-1)
e+=1
print(s)
print(x)
u+=1
return e
hello(4)
现在了解该计划的部分内容:
现在让我们看看什么是堆栈以及什么是堆栈部分:
堆栈的分配:
记住一件事,如果任何函数得到“返回”,无论它已经加载了所有本地变量,或者它将立即从堆栈中返回的任何东西将是他的堆栈帧。这意味着当任何递归函数获得基本条件并且我们在基本条件之后放置返回因此基本条件将不等待加载位于程序的“else”部分中的局部变量它将立即从堆栈返回当前帧,现在如果一个帧返回下一帧是激活记录。 实际上看到这个:
重新分配区块:
所以现在每当函数找到return语句时,它都会从堆栈中删除当前帧。
从堆栈值返回时,将按照它们在堆栈中分配的顺序的相反顺序返回。
答案 2 :(得分:40)
快速总结。也许有人有更好的解释。
调用堆栈由1个或多个堆栈帧组成。每个堆栈帧对应于对尚未以返回终止的函数或过程的调用。
要使用堆栈帧,线程会保留两个指针,一个称为堆栈指针(SP),另一个称为帧指针(FP)。 SP始终指向堆栈的“顶部”,FP始终指向帧的“顶部”。另外,线程还维护程序计数器(PC),该程序计数器指向要执行的下一条指令。
以下内容存储在堆栈中:局部变量和临时值,当前指令的实际参数(过程,函数等)
关于清理堆栈有不同的调用约定。
答案 3 :(得分:13)
“调用堆栈由堆栈帧组成......” - Wikipedia
堆栈框架是您放在堆栈上的东西。它们是包含要调用的子例程的信息的数据结构。
答案 4 :(得分:3)
程序员可能对堆栈帧的问题不是很广泛(它是堆栈中的一个单独的实体,仅用于一个函数调用并保留返回地址,参数和局部变量)但在狭义上 - 当术语在编译器选项的上下文中提到stack frames
。
问题的作者是否有意,但编译器选项方面的堆栈框架的概念是一个非常重要的问题,这里没有其他答复所涵盖。
例如,Microsoft Visual Studio 2015 C / C ++编译器具有与stack frames
相关的以下选项:
英特尔C ++编译器具有以下功能:
具有以下别名:
Delphi具有以下命令行选项:
从特定意义上讲,从编译器的角度来看,堆栈帧只是例程的入口和出口代码,它将一个锚推送到堆栈 - 它也可以用于调试和用于异常处理。调试工具可以扫描堆栈数据并使用这些锚点进行回溯,同时在堆栈中定位call sites
,即按照它们被分层调用的顺序显示函数的名称。对于英特尔架构,输入为push ebp; mov ebp, esp
或enter
,退出为mov esp, ebp; pop ebp
或leave
。
这就是为什么理解程序员在编译器选项时堆栈框架是什么非常重要的原因 - 因为编译器可以控制是否生成此代码。
在某些情况下,编译器可以省略堆栈帧(例程的入口和出口代码),并且可以通过堆栈指针(SP / ESP / RSP)直接访问变量,而不是方便的基址指针(BP / ESP / RSP)。 遗漏堆栈帧的条件,例如:
省略堆栈帧(例程的进入和退出代码)可以使代码更小更快,但它也可能会对调试器回溯堆栈中的数据并将其显示给程序员的能力产生负面影响。这些是编译器选项,用于确定函数应在哪些条件下具有入口和出口代码,例如:(a)始终,(b)从不,(c)在需要时(指定条件)。
答案 5 :(得分:-1)
堆栈帧是与函数调用相关的压缩信息。此信息通常包括传递给函数的参数,局部变量以及终止时返回的位置。激活记录是堆栈帧的另一个名称。堆栈帧的布局由制造商在ABI中确定,并且支持ISA的每个编译器必须符合该标准,但是布局方案可以依赖于编译器。一般来说,堆栈框架的大小不受限制,但有一个名为"红色/受保护区域"允许系统调用等执行而不会干扰堆栈帧。
总有一个SP,但在某些ABI上(例如ARM和PowerPC)FP是可选的。需要放置到堆栈的参数可以仅使用SP来抵消。是否为函数调用生成堆栈帧取决于参数的类型和数量,局部变量以及通常如何访问局部变量。在大多数ISA上,首先使用寄存器,如果参数多于专用于传递参数的寄存器,则将它们放在堆栈上(例如,x86 ABI有6个寄存器来传递整数参数)。因此,有时候,某些函数不需要将堆栈帧放在堆栈上,只需将返回地址压入堆栈。