我听到一个神话说无限循环或没有停止条件的递归函数将在“堆栈溢出”时停止。是不是?
例如:
void call()
{
call();
}
或
for(;;){;}
当堆栈溢出时它们真的会停止吗?
更新:如果它真的停止了,我可以在多少次递归调用后检测到它?
答案 0 :(得分:5)
你的第一个例子是一个递归方法调用 - 每次调用call
(在大多数语言和环境中)都会创建一个新的堆栈帧,最终你会用完堆栈(你会得到一个堆栈)溢出情况)。
你的第二个例子没有涉及递归方法调用 - 它只是一个循环。不需要创建堆栈帧,堆栈也不会溢出。
给你的例子一个镜头 - 第一个例子将导致堆栈溢出比你想象的更快。第二个会让你的粉丝真的快速旋转,但除非你杀了它,否则什么都不会发生。
答案 1 :(得分:4)
这实际上取决于语言的选择。
在某些语言中,无限递归函数将根据系统或语言相关的条件停止堆栈溢出。这样做的原因是函数调用和返回的许多实现为每个函数调用分配新的空间,并且当空间耗尽时,程序将失败。但是,其他语言(Scheme和各种gcc优化级别)实际上会让这个程序永远运行,因为它们足够聪明,可以意识到它们可以为每次调用重用空间。
在某些语言中,无限循环将永远运行。您的程序将继续运行,永远不会取得进展。在其他语言中,允许编译器优化无限循环。例如,新的C ++ 0x标准表示允许编译器假定任何循环将终止或进行一些全局可见的操作,因此如果编译器看到无限循环,它可能只是优化循环存在,所以循环实际上终止了。
换句话说,它真的取决于。这个问题没有硬性答案。
希望这有帮助!
答案 2 :(得分:0)
取决于您使用的语言,当达到最大内存或达到最大执行时间时,循环将结束。有些语言会检测到无限循环并阻止其运行。
P.S。这不是一个神话。你可以尝试一下。
答案 3 :(得分:0)
“更新:如果它真的停止了,我可以在多少次递归调用后检测到它?”
你当然可以:
call(int n){
print(n)
call (n+1)
}
然后打电话:
call(1)
发生堆栈溢出时,请查看最后打印的数字。这是您拥有的方法调用次数。
希望这有帮助!
N.S。
答案 4 :(得分:-1)
无论使用哪种语言,当我创建一个可能无限的循环时,我总是在其中构建“紧急出口”。我已经在C#,VB.Net,VBA,JAVA中完成了此操作,这是第一次在IBM DB2 SQL函数中进行。
由于该概念在任何语言中均有效,因此我将以伪代码来表示它,如下所示:
Begin Procedure
Declare Variable safety_counter As Integer
Begin loop
...
<body of code>
If some_criteria = True Then <-- this is your normal exit
Exit Loop
End If
...
safety_counter = safety_counter + 1
If safety_counter >= 1000 <-- set to any value you wish
Exit Loop <-- this is the emergency exit
End If
End Loop
一些变化如下。
如果循环很简单,则两个退出条件可能会写在同一If-Then中:
If some_criteria = True Then <-- normal exit
Or safety_counter >= 1000 <-- emergency exit
Exit Loop
End If
safety_counter = safety_counter + 1
可能会返回错误值,如下所示:
Begin Procedure
Declare Variable result_value As Integer
Declare Variable safety_counter As Integer
Begin loop
...
<body of code>
If some_criteria = True Then <-- this is your normal exit
Set result_value = abc * xyz <-- the value you seek
Exit Loop
End If
...
safety_counter = safety_counter + 1
If safety_counter >= 1000 <-- set to any sufficient value
Set result_value = (-123) <-- indicate "infinite loop error"
Exit Loop <-- this is the emergency exit
End If
End Loop
Return result_value
有很多方法可以做到这一点,但是关键是在safety_counter中,以及监视它以触发紧急出口的方法。