Ton*_*y R 178
堆栈帧是一个被推入堆栈的数据帧.在调用堆栈的情况下,堆栈帧将表示函数调用及其参数数据.
如果我没记错的话,首先将函数返回地址压入堆栈,然后将局部变量的参数和空间压入堆栈.它们共同构成了"框架",尽管这可能取决于架构.处理器知道每帧中有多少字节并相应地移动堆栈指针,因为帧被推出并从堆栈中弹出.
高级调用堆栈与处理器调用堆栈之间存在很大差异.
当我们谈论处理器的调用堆栈时,我们讨论的是在汇编或机器代码中处理字节/字级别的地址和值.在讨论高级语言时有"调用堆栈",但它们是由运行时环境管理的调试/运行时工具,因此您可以记录程序出错(高级别).在这个级别,行号,方法和类名之类的东西通常是已知的.当处理器获得代码时,它绝对没有这些东西的概念.
Aad*_*Ura 65
如果您非常了解堆栈,那么您将了解内存在程序中的工作方式,如果您了解内存在程序中的工作原理,您将了解函数存储在程序中的方式,如果您了解函数存储在程序中的方式,您将理解递归函数的工作原理以及是否你理解递归函数是如何工作的,你会理解编译器是如何工作的,如果你理解编译器如何工作,你的思想将作为编译器工作,你将很容易地调试任何程序
让我解释一下堆栈的工作原理:
首先,你必须知道堆栈中的函数存储:
堆存储动态内存分配值.堆栈存储自动分配和删除值.
让我们用例子来理解:
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)
Run Code Online (Sandbox Code Playgroud)
现在了解这个程序的一部分:
现在让我们看看什么是堆栈以及什么是堆栈部分:
堆栈的分配:
记住一件事,如果任何函数得到"返回",无论它已经加载了所有本地变量,或者它将立即从堆栈中返回的任何东西将是他的堆栈帧.这意味着当任何递归函数获得基本条件并且我们在基本条件之后放置返回因此基本条件将不等待加载位于程序的"else"部分中的局部变量它将立即从堆栈返回当前帧,现在如果一个帧返回下一帧是激活记录.在实践中看到这个:
释放块:
所以现在每当函数找到return语句时,它都会从堆栈中删除当前帧.
从堆栈值返回时,将按照它们在堆栈中分配的顺序的相反顺序返回.
这些是非常简短的描述,如果你想更深入地了解堆栈和双递归,请阅读本博客的两篇文章:
erv*_*her 42
快速总结.也许有人有更好的解释.
调用堆栈由1个或多个堆栈帧组成.每个堆栈帧对应于对尚未以返回终止的函数或过程的调用.
要使用堆栈帧,线程会保留两个指针,一个称为堆栈指针(SP),另一个称为帧指针(FP).SP始终指向堆栈的"顶部",FP始终指向帧的"顶部".另外,线程还维护程序计数器(PC),该程序计数器指向要执行的下一条指令.
以下内容存储在堆栈中:局部变量和临时值,当前指令的实际参数(过程,函数等)
关于清洁堆栈有不同的调用约定.
程序员可能对栈框架的问题不是广义上的(它是栈中的一个单一实体,仅服务于一个函数调用,并保留返回地址,自变量和局部变量),而是一个狭义的问题–在该术语stack frames中提到编译器选项的上下文。
这个问题的作者是否有这个意思,但是从编译器选项的角度来看,堆栈框架的概念是一个非常重要的问题,此处其他答复未涉及。
例如,Microsoft Visual Studio 2015 C / C ++编译器具有以下与以下相关的选项stack frames:
GCC具有以下功能:
英特尔C ++编译器具有以下功能:
具有以下别名:
Delphi具有以下命令行选项:
从特定的角度来看,从编译器的角度来看,堆栈帧只是例程的入口和出口代码,它将锚点压入堆栈-也可以用于调试和异常处理。调试工具可以扫描堆栈数据,并在定位call sites到堆栈中时使用这些定位符进行回溯,即以分层调用的顺序显示功能名称。对于Intel体系结构,它是push ebp; mov ebp, esp或enter表示进入和/ mov esp, ebp; pop ebp或leave退出。
因此,对于程序员而言,了解编译器选项中的堆栈框架非常重要,因为编译器可以控制是否生成此代码。
在某些情况下,编译器可以忽略堆栈帧(例程的进入和退出代码),并且将直接通过堆栈指针(SP / ESP / RSP)而不是便捷的基本指针(BP / ESP / RSP)。省略堆叠框架的条件,例如:
省略堆栈帧(例程的进入和退出代码)可以使代码更小,更快,但是也可能会对调试器回溯堆栈中的数据并将其显示给程序员的能力产生负面影响。这些是编译器选项,用于确定函数应在哪些条件下具有进入和退出代码,例如:(a)始终,(b)从不,(c)必要时(指定条件)。