编译器如何知道函数调用后控制应该返回到哪里?

How does the compiler know where control should return to after a function call?

本文关键字:返回 何知道 函数调用 控制 编译器      更新时间:2023-10-16

考虑以下函数:

int main()
{
    //statement(s);
    func1();
    //statement(s);
}
void func1()
{
    //statement(s);
    func2();
    //statement(s);
}
void func2()
{
    //statement(s);
}

编译器如何知道在func2执行了所有操作后返回到哪里?我知道控制转移到函数func1(以及确切的哪个语句),但是编译器如何知道它?什么告诉编译器返回到哪里?

这通常使用调用堆栈:

实现
  • 当控制被转移到一个函数时,返回的地址被压入堆栈。
  • 当函数结束时,该地址从堆栈中弹出,并用于将控制权转移回被调用方。

详细信息通常由编译代码的硬件体系结构强制执行。

实际上,编译器不运行代码,但是机器运行代码,当它调用一个新函数时,它将下一个指令的地址存储在当前被调用的函数之后的堆栈中,因此当函数返回时,它可以将其弹出回指令指针(IP)并从那里恢复。

为了便于解释,我做了一些简化。

当调用函数时,调用函数中的正确返回地址被放置在某个地方,通常是堆栈(尽管标准没有强制这样做),堆栈正是用于存储返回地址的目的。

编译器有责任确保它的调用约定是这样的,除非出现错误(例如,堆栈溢出),那么被调用的函数知道如何返回到调用函数。

运行时使用了一些被称为'调用堆栈'的东西,它基本上保存了被调用函数返回后要调用的下一个语句的地址。因此,当进行函数调用时,在控制跳转到新指令地址之前,调用函数中的下一个指令地址被推入堆栈。这个过程在以后对任何函数的每次调用中都要重复。为什么只有一堆呢?因为它需要回到它离开的地方,这基本上是一个'后进先出'的行为,堆栈是做那个的数据结构。当你在Visual Studio中调试一个程序时,你可以看到这个调用堆栈——有一个叫做"调用堆栈"的单独窗口,它显示了放置在调用堆栈中的地址项。