我最近想到了这种堆栈溢出的情况:
int f()
{
return f();
}
int main(void)
{
f();
return 0;
}
这肯定会使程序崩溃。但我的问题是为什么这与无限循环不同?在返回行递归调用的情况下,编译器可以意识到不需要保留调用函数的任何信息,因为被调用函数的返回点与调用者的返回点相同。 现在,在这种情况下,我同意编译器需要在堆栈中保留进行调用的函数的信息:
int f()
{
int x = f();
return x;
}
int main(void)
{
f();
return 0;
}
我肯定遗漏了什么,如果有人向我解释,我将不胜感激。问候
最佳答案
事实证明,在某些编译器中,使用正确的优化标志,这实际上不会导致堆栈溢出!事实上,我尝试在 g++
中编译您的程序。在默认优化下,它会导致堆栈溢出,但在优化级别 -O3
下,它只会进入无限循环。
无限递归和无限循环之间存在差异的原因与默认情况下编译器如何实现这些结构有关。历史上,循环是使用分支指令实现的,这些指令告诉处理器在程序的不同部分开始执行。所有这些指令所做的就是将程序计数器跳转到其他地方,这只是修改寄存器的内容。而函数调用是通过在堆栈中添加一个新的激活记录来实现的,对参数、返回地址等进行编码,以便函数返回时,它知道返回到哪里。
也就是说,这不是必须实现函数调用或分支的“方式”。理论上,您可以通过使用函数调用和返回来实现循环,但没有编译器这样做。同样,对于尾递归函数(如您的示例),编译器通常足够聪明,可以省略所有堆栈操作并将其转换为简单的分支指令,以避免堆栈设置和拆卸的开销。
简而言之,您获得不同行为的原因是基于编译器如何决定实现代码。如果它足够聪明,看到它不需要进行昂贵的函数调用设置,那么它会将调用转换为简单的循环指令并永远循环。如果它没有检测到这一点,那么它将退回到朴素的函数调用机制。
关于c - 在这种情况下,无限递归调用应该引发堆栈溢出吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5345594/