了解C / C ++中的函数调用堆栈框架吗?


19

我试图了解如何构建堆栈框架以及哪些变量(参数)以什么顺序被压入堆栈?一些搜索结果表明,C / C ++编译器根据函数内执行的操作进行决策。例如,如果假定该函数只是将传入的int值加1(类似于++运算符)并返回它,则它将把该函数的所有参数和局部变量放入寄存器中。

我想知道哪些寄存器用于返回或按值传递参数。引用如何返回?编译器如何在eax,ebx,ecx和edx之间进行选择?

我需要了解什么才能了解​​函数调用期间如何使用,构建和销毁寄存器,堆栈和堆引用?


这很难读(文本墙)。您介意将帖子编辑成更好的形状吗?
t

1
对我来说,这个问题似乎相当广泛。另外,这不是非常特定于平台的吗?
卡扎尔克


Answers:


11

除了Dirk所说的以外,堆栈帧的一个重要用途是保存寄存器的先前值,以便可以在函数调用后恢复它们。因此,即使在使用寄存器传递参数,返回值并保存返回地址的处理器上,这些寄存器的值也将在函数调用之前保存在堆栈中,以便可以在调用之后将其恢复。这样一来,一个函数可以调用另一个函数,而无需覆盖其自身的参数或忘记其自身的返回地址。

因此,在典型的“通用”系统上从函数A调用函数B可能涉及以下步骤:

  • 功能A:
    • 推动返回值的空间
    • 推送参数
    • 推回寄地址
  • 跳至功能B
  • 功能B:
    • 推送前一个堆栈帧的地址
    • 推送此函数使用的寄存器的值(以便可以恢复它们)
    • 推动局部变量的空间
    • 做必要的计算
    • 恢复寄存器
    • 恢复先前的堆栈帧
    • 存储功能结果
    • 跳转到寄信人地址
  • 功能A:
    • 弹出参数
    • 弹出返回值

这绝不是函数调用可以工作的唯一方法(我可能会失调一两步),但是它应该使您了解如何使用堆栈来让处理器处理嵌套的函数调用。


“推”在这里到底是什么意思?我不知道该怎么做。
托马什Zato -恢复莫妮卡

2
@TomášZato pushpop是堆栈上的两个基本操作。堆栈是后进先出的结构,就像一books书。当您使用时push,您将一个新的对象放在堆栈的顶部。当您pop从堆栈顶部取出一个对象时。不允许在中间插入或删除对象,只能在堆栈的顶部进行操作。您可能会在Wikipedia上阅读更多有关堆栈的信息,尤其是有关程序堆栈的信息
卡雷布(Caleb)

11

这取决于所使用的调用约定。定义调用约定的人都可以根据自己的意愿做出此决定。

在x86上最常见的调用约定中,不使用寄存器来传递参数。从最右边的参数开始,将这些参数压入堆栈。返回值放在eax中,如果需要额外的空间,可以使用edx。引用和指针都以eax中的地址形式返回。


5

如果您非常了解堆栈,那么您将了解内存在程序中的工作方式,如果您了解内存在程序中的工作方式,则将了解函数在程序中的存储方式,如果您了解函数在程序中的存储方式,则将了解递归函数的工作方式以及是否您了解递归函数的工作原理,您将了解编译器的工作原理,如果您了解编译器的工作原理,您的想法将像编译器一样工作,并且您将很容易调试任何程序

让我解释一下堆栈是如何工作的:

首先,您必须知道函数如何存储在堆栈中:

堆存储动态内存分配值。堆栈存储自动分配和删除值。

在此处输入图片说明

让我们用例子来理解:

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)

现在了解该程序的各个部分:

在此处输入图片说明

现在让我们看看什么是堆栈以及什么是堆栈部分:

在此处输入图片说明

堆栈分配:

记住一件事,无论任何函数加载了他的所有本地变量,或者任何将立即从堆栈返回的东西都将返回其堆栈框架,该函数都会“返回”。这意味着当任何递归函数获得基本条件并且我们在基本条件之后放置return时,这样基本条件将不会等待加载位于程序“ else”部分中的局部变量,它将立即从堆栈中返回当前帧,现在如果返回一帧返回下一帧在激活记录中。实际看到这一点:

在此处输入图片说明

块的解除分配:

因此,现在无论何时找到返回语句的函数,它都会从堆栈中删除当前帧。

从堆栈返回时,值将以它们在堆栈中分配的顺序相反的顺序返回。

在此处输入图片说明

这些描述非常简短,如果您想更深入地了解堆栈和双递归,请阅读此博客的两篇文章:

有关逐步堆栈的更多信息

有关使用堆栈逐步进行双递归的更多信息


3

您在寻找什么叫做应用程序二进制接口 -ABI。

每个编译器都有一个说明ABI的规范。

每个平台通常会指定和ABI以便支持编译器之间的互操作性。例如,x86调用约定阐明了x86和x86-64的典型调用约定。我希望能得到比维基百科更正式的文档。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.