我在开发ac#.net程序时遇到了问题,我把它简化为一个简单的问题,我需要理解为什么这个代码抛出一个stackoverflow异常,如果我调用这样的函数:
CheckFunc(16000);
但如果我这样称呼它就可以正常工作
CheckFunc(1000);
这是功能:
private void CheckFunc(Int32 i)
{
if (i == 0)
MessageBox.Show("good");
else
CheckFunc(i - 1);
}
尝试使代码尽可能简单......
我知道有一个堆栈溢出但堆栈?我该如何解决这个问题?
感谢。
答案 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个级别。