被调用的函数在调用后如何返回给调用者?

12

我读到当程序调用一个函数时,被调用的函数必须知道如何返回给它的调用者。

我的问题是:被调用的函数如何知道如何返回给它的调用者?在编译器后面是否有机制在起作用?


1
你应该阅读这个链接:调用栈结构。这是一个非常好的答案:第11章--过程。如果你喜欢视频,那么你会喜欢这里的所有视频,包括:黑客组装入门(第11部分)函数堆栈 - Grijesh Chauhan
4个回答

12
编译器遵循特定的“调用约定”,作为目标ABI的一部分进行定义。该调用约定将包括一种系统知道要返回的地址的方法。通常,调用约定利用硬件对过程调用的支持。例如,在Intel上,返回地址被推入堆栈中:

...处理器将EIP寄存器的值(其中包含CALL指令后面的指令的偏移量)推送到堆栈上(稍后用作返回指令指针)。

通过ret指令从函数返回:

... 处理器将返回指令指针(偏移量)从堆栈顶部弹出到EIP寄存器,并开始在新的指令指针处执行程序。

相比之下,在ARM上,返回地址放在链接寄存器中:

BLBLX指令将下一条指令的地址复制到lr寄存器(链接寄存器,也就是r14)中。

常见的返回操作是执行movs pc, lr指令,将链接寄存器中的地址复制回程序计数器寄存器。

参考资料:

  1. Intel软件开发人员手册
  2. ARM信息中心

8
  1. 编译器知道如何调用函数以及使用哪种调用约定。例如,在C语言中,函数的参数被推到堆栈上。调用者负责清除堆栈,因此被调用的函数不必删除参数。其他调用约定可能包括将参数推到堆栈上,而被调用的函数必须清理它。在这种情况下,生成的代码是这样的,即函数在返回之前会修正堆栈。其他调用约定可能会将参数传递给寄存器,因此在这种情况下,被调用的函数也不必处理。

  2. CPU有一种机制可以调用子程序。这将在堆栈上存储当前执行地址,然后将处理转移到新地址。当函数完成时,它执行一个返回语句,该语句将获取调用者地址并在那里恢复执行。

如果由于堆栈未正确清除或内存被覆盖而破坏了返回地址,则会出现未定义行为。当然,具体的实现细节取决于所使用的平台。


4
这是由堆栈实现的(特别是在类似英特尔的系统上)。假设我们有一个包含 int 的本地变量的方法 caller
caller(调用 target(时,该int必须被保存。它与调用地址一起放置在堆栈上。 target(可以执行其逻辑,创建其自己的局部变量并调用其他方法。其局部变量将与调用地址一起放置在堆栈上。
target(结束时,堆栈会“展开”。包含 target(的局部变量的堆栈顶部将被移除。
当方法递归太深时,堆栈可能会增长过大,从而可能发生“堆栈溢出”。

4

这需要调用方和被调用方之间的合作。

调用方同意将被调用方应该返回的地址提供给被调用方(通常通过将其推送到堆栈上或通过寄存器传递),而被调用方同意在执行完成后返回到该地址。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接