我读到当程序调用一个函数时,被调用的函数必须知道如何返回给它的调用者。
我的问题是:被调用的函数如何知道如何返回给它的调用者?在编译器后面是否有机制在起作用?
我读到当程序调用一个函数时,被调用的函数必须知道如何返回给它的调用者。
我的问题是:被调用的函数如何知道如何返回给它的调用者?在编译器后面是否有机制在起作用?
通过...处理器将
EIP
寄存器的值(其中包含CALL
指令后面的指令的偏移量)推送到堆栈上(稍后用作返回指令指针)。
ret
指令从函数返回:相比之下,在ARM上,返回地址放在链接寄存器中:... 处理器将返回指令指针(偏移量)从堆栈顶部弹出到
EIP
寄存器,并开始在新的指令指针处执行程序。
BL
和BLX
指令将下一条指令的地址复制到lr
寄存器(链接寄存器,也就是r14
)中。
常见的返回操作是执行movs pc, lr
指令,将链接寄存器中的地址复制回程序计数器寄存器。
参考资料:
编译器知道如何调用函数以及使用哪种调用约定。例如,在C语言中,函数的参数被推到堆栈上。调用者负责清除堆栈,因此被调用的函数不必删除参数。其他调用约定可能包括将参数推到堆栈上,而被调用的函数必须清理它。在这种情况下,生成的代码是这样的,即函数在返回之前会修正堆栈。其他调用约定可能会将参数传递给寄存器,因此在这种情况下,被调用的函数也不必处理。
CPU有一种机制可以调用子程序。这将在堆栈上存储当前执行地址,然后将处理转移到新地址。当函数完成时,它执行一个返回语句,该语句将获取调用者地址并在那里恢复执行。
如果由于堆栈未正确清除或内存被覆盖而破坏了返回地址,则会出现未定义行为。当然,具体的实现细节取决于所使用的平台。
int
的本地变量的方法 caller
。 caller(
调用 target(
时,该int必须被保存。它与调用地址一起放置在堆栈上。 target(
可以执行其逻辑,创建其自己的局部变量并调用其他方法。其局部变量将与调用地址一起放置在堆栈上。 target(
结束时,堆栈会“展开”。包含 target(
的局部变量的堆栈顶部将被移除。这需要调用方和被调用方之间的合作。
调用方同意将被调用方应该返回的地址提供给被调用方(通常通过将其推送到堆栈上或通过寄存器传递),而被调用方同意在执行完成后返回到该地址。