通过递归的stackoverflow

时间:2012-07-03 13:07:20

标签: c# exception recursion stack-overflow

  

可能重复:
  Runtime exception, recursion too deep

我在开发ac#.net程序时遇到了问题,我把它简化为一个简单的问题,我需要理解为什么这个代码抛出一个stackoverflow异常,如果我调用这样的函数:

CheckFunc(16000);

但如果我这样称呼它就可以正常工作

CheckFunc(1000);

这是功能:

private void CheckFunc(Int32 i)
{
    if (i == 0)
        MessageBox.Show("good");
    else
        CheckFunc(i - 1);
}

尝试使代码尽可能简单......

我知道有一个堆栈溢出但堆栈?我该如何解决这个问题?

感谢。

7 个答案:

答案 0 :(得分:12)

问题确实是堆栈溢出

为什么会这样:

堆栈是一个特殊的内存区域,存储了一些内容:

  • 函数的局部变量
  • 功能参数
  • (最重要的)函数返回地址。当您从函数返回时,这就是处理器知道返回的位置。

问题在于此内存区域有限。递归调用将在此堆栈上添加大量数据,快速填充它。

如何修复它:

有几种方法:

  • 减少变量的数量,如果你在递归函数中有一个本地数组,那你就是在寻找麻烦。
  • 减少功能的参数数量(显然不是这里的情况)
  • 减少递归调用次数。

如果这还不够,唯一的解决方案就是找到一个迭代解决方案。

答案 1 :(得分:11)

这是因为你没有足够的堆栈空间来递归16000次。

递归应该几乎总是低于这个水平!否则,重写为循环。你不能以任何其他方式解决这个问题。

答案 2 :(得分:2)

您需要了解堆栈以及它在程序执行中的使用方式。简而言之,您的函数失败,因为它递归调用自身太多次。堆栈与堆一样,具有有限的大小,但与堆不同,它通常要小得多。每次函数调用自身时,堆栈中的某些内存用于保存函数调用和函数本地变量的信息(在示例中对变量i的引用)。这称为堆栈帧。当由于递归太深而创建的堆栈帧太多时,会出现堆栈溢出。

你应该删除CheckFunc中的递归,而是从循环中调用它。

答案 3 :(得分:0)

C#中有Call Stack的限制。在第一种情况下,你超过了这个数字,所以你得到 Stack Overflow Exception

没有办法解决这个问题,但你自然可以通过减少递归的深度来逃避它。

答案 4 :(得分:0)

我更愿意迭代。如果你有16000个递归步骤,那将是非常缓慢的(我认为)。

答案 5 :(得分:0)

调用堆栈溢出。

每当你调用一个函数(或者编程语言所谓的“子程序”,“方法”等)时,CPU都会存储函数完成后它必须返回的位置的地址。此外,“本地”变量或参数通常也存储在堆栈中。

该堆栈具有固定大小,通常可以通过一些魔法增加 - 当您创建线程时,通常可以提供堆栈大小,当您链接程序时,可能还有一个链接器选项来设置堆栈大小

基本上,您的代码在堆栈上创建了16000个“i”副本,以及16000个返回指针,以及编译器在函数调用时可能存储在堆栈中的任何内容。在你的另一次尝试中,你只制作了1000份这些东西。

当然,你正在做一些叫做“尾递归”的东西,它应该被优化掉;不要问我为什么你的编译器不这样做。

答案 6 :(得分:0)

这基本上是因为你使用递归是错误的。

溢出的堆栈是进程的调用堆栈。堆栈用于发送给方法的参数,调用的返回地址以及方法中的局部变量。

对于每次调用,都会将其添加到堆栈中:

+---------------------+
|                     |
         ...
|                     |
+---------------------+  <-- stack pointer before call
| parameter: int      |
+---------------------+
| return address      |
+---------------------+
| stack frame for     |
|   local variables   |
+---------------------+  <-- stack pointer in the call

每个递归调用都会向堆栈中添加另一个块,并且由于堆栈空间有限(到2 MB IIRC),如果你进行了几千个深度的递归,你将填满堆栈。

当以一种好的方式使用递归时,您宁愿尝试将数据拆分为一半,而不是削减一块。基本上是:

private void CheckFunc(int i) {
  if (i == 0) {
    MessageBox.Show("good");
  } else {
    CheckFunc(i / 2);
  }
}

对于任何整数值,此递归永远不会超过31个级别。