Jus*_*ste 3 c stack-overflow recursion
我最近想过这种堆栈溢出的情况:
int f()
{
return f();
}
int main(void)
{
f();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这肯定会导致程序崩溃.但我的问题是为什么这与无限循环不一样?在返回行的递归调用的情况下,编译器可以意识到不需要保留调用函数的任何信息,因为被调用函数的返回点与调用者的返回点相同.现在,在这种情况下,我同意编译器需要保留在堆栈中进行调用的函数的信息:
int f()
{
int x = f();
return x;
}
int main(void)
{
f();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
当然,我错过了一些东西,如果有人向我解释,我会很感激.问候
事实证明,在一些编译器中,使用正确的优化标志,这实际上不会导致堆栈溢出!事实上,我尝试编译你的程序g++.在默认情况下进行优化时,会导致堆栈溢出,但在优化级别,-O3它只会进入无限循环.
无限递归和无限循环之间存在差异的原因与默认情况下编译器如何实现这些结构有关.历史上已经使用分支指令实现循环,该分支指令告诉处理器在程序的不同部分获取执行.所有这些指令都是在其他地方跳转程序计数器,这只是修改寄存器的内容.另一方面,函数调用是通过向堆栈添加新的激活记录来实现的,以对参数,返回地址等进行编码,以便在函数返回时知道返回的位置.
也就是说,这不是必须实现函数调用或分支的"方式".理论上,你可以通过使用函数调用和返回来实现循环,尽管没有编译器这样做.类似地,对于尾递归的函数(如您的示例所示),编译器通常足够聪明以消除所有堆栈操作并将其转换为简单的分支指令以避免堆栈设置和拆除的开销.
简而言之,您获得不同行为的原因取决于编译器如何决定实现代码.如果足够聪明地看到它不需要进行昂贵的函数调用设置,那么它会将调用转换为简单的循环指令并永远循环.如果它没有检测到这个,那么它将回退到天真的函数调用机制.