5

考虑以下函数:

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

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

void func2()
{
    //statement(s);
}

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

4

4 回答 4

15

这通常使用调用堆栈来实现:

  • 当控制权转移到函数时,要返回的地址被压入堆栈。
  • 当函数完成时,地址从堆栈中弹出并用于将控制权转移回被调用者。

详细信息通常由正在为其编译代码的硬件架构规定。

于 2013-03-07T14:58:28.173 回答
3

实际上,编译器并没有运行代码,但是机器运行,并且当它调用一个新函数时,它会将当前正在调用的函数之后要执行的下一条指令的地址存储在堆栈上,这样当函数返回它可以将其弹出回指令指针(IP)并从那里恢复。

为了解释起见,我已经简化了一些事情。

于 2013-03-07T15:00:14.893 回答
2

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

编译器有责任确保其调用约定是这样的,除非出现问题(例如,堆栈溢出),否则被调用函数知道如何返回调用函数。

于 2013-03-07T14:58:46.757 回答
1

运行时使用一些称为“调用堆栈”的东西,它基本上保存了在被调用函数返回后要调用的下一条语句的地址。因此,当进行函数调用时,在控件跳转到新指令地址之前,调用函数中的下一条指令地址被压入堆栈。并且每次后续调用任何函数都会重复此过程。现在为什么只有一个堆栈?因为有必要回到它停止的地方——这基本上是一种“后进先出”的行为,而堆栈是执行此操作的数据结构。当您在 Visual Studio 中调试程序时,您实际上可以查看此调用堆栈 - 有一个名为“调用堆栈”的单独窗口,它显示放置在调用堆栈中的地址条目。

于 2013-03-07T15:06:39.050 回答