【问题标题】:When will infinite loops, and recursive functions with no stop conditions, eventually stop?无限循环和没有停止条件的递归函数何时会最终停止?
【发布时间】:2011-07-31 03:26:21
【问题描述】:

我听说过一个神话,当“堆栈溢出”时,无限循环或没有停止条件的递归函数将停止。对吗?

例如:

void call()
{
call(); 
} 

for(;;){;}

堆栈溢出时它们真的会停止吗?

更新:如果真的停止了,我可以在多少次递归调用后检测到?

【问题讨论】:

    标签: recursion stack-overflow infinite-loop


    【解决方案1】:

    这真的取决于语言的选择。

    在某些语言中,您的无限递归函数将根据系统或语言相关条件因堆栈溢出而停止。原因是函数调用和返回的许多实现为每个函数调用分配新空间,当空间用完时程序将失败。但是,其他语言(Scheme 和各种 gcc 优化级别)实际上会让这个程序永远运行,因为它们足够聪明,可以实现每次调用都可以重用空间。

    在某些语言中,无限循环将永远运行。您的程序将继续运行,永远不会取得进展。在其他语言中,编译器允许优化无限循环。例如,C++ 标准规定编译器可以假设任何循环都将终止或执行一些全局可见的操作,因此如果编译器看到无限循环,它可能只是优化循环不存在,所以循环确实终止了。

    换句话说,这真的取决于。这个问题没有固定的答案。

    希望这会有所帮助!

    【讨论】:

      【解决方案2】:

      您的第一个示例是递归方法调用 - 每次调用 call(在大多数语言和环境中)都会创建一个新的堆栈帧,最终您将用完堆栈(您将获得堆栈溢出条件)。

      您的第二个示例不涉及递归方法调用 - 它只是一个循环。无需创建栈帧,栈不会溢出。

      试一试您的示例 - 第一个示例导致堆栈溢出的速度比您想象的要快。第二个会让你的粉丝旋转得非常快,但除非你杀死它,否则什么都不会发生。

      【讨论】:

      • 好的,那么有没有一种方法可以检测它会在多少堆栈帧之后流动?
      • 是的。你的计算机持有一个指向栈顶的指针,并且知道栈的最大地址。当它被要求创建一个新的堆栈帧时,它会检查堆栈指针的新值是否不超过该最大地址。
      • 关于您的编辑 - 我想您可以通过了解进程的堆栈大小、计算/发现特定平台和方法签名的堆栈帧大小以及做一些数学运算来预测堆栈何时溢出。但是你为什么要这样做呢?除非您的程序有故障,否则您不应该遇到堆栈溢出。有一些例外,但它们确实很少见。如果您的堆栈增长得那么大,而且这不是程序错误,请查看尾调用优化,如下面的 icktoofay 所述。
      • 第一个可能会通过尾调用优化进行优化,防止堆栈溢出。
      【解决方案3】:

      取决于您使用的语言,当达到最大内存分配或最大执行时间时,循环将结束。某些语言会检测到无限循环并阻止它运行。

      附言这不是神话。你可以试试看。

      【讨论】:

      • 为什么有些语言会检测到它并阻止它运行,你有什么例子吗?
      • xCode 中的 Objective-C 怎么样?它在分析模式下检测到无限循环。
      【解决方案4】:

      “更新:如果真的停止了,我可以检测多少次递归调用后?”

      你当然可以:

      call(int n){
          print(n)
          call (n+1)
      }
      

      然后只需调用:

      call(1)
      

      发生堆栈溢出时,查看最后打印的数字。这是您的方法调用次数。

      希望这会有所帮助!
      NS

      【讨论】:

        【解决方案5】:

        无论使用哪种语言,当我创建一个可能无限的循环时,我总是会在其中建立一个“紧急出口”。我已经在 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,以及监控它以触发紧急出口的方式。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-08-30
          • 2013-03-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-05-30
          相关资源
          最近更新 更多