c++递归在没有明显原因的情况下退出
我用递归写了一个函数。在测试时发现,该函数在没有任何明显原因的情况下被终止,而递归仍在运行。
为了测试这一点,我编写了一个无限递归。
在我的电脑上,该函数在大约 2 秒后退出,最后的输出约为 327400。 最后一个数字并不总是相同。
我使用 Ubuntu Lucid Lynx、GCC 编译器和 Eclipse 作为 IDE。如果有人知道问题是什么以及我如何阻止程序退出,我会非常高兴。
#include <iostream>
void rek(double x){
std::cout << x << std::endl;
rek(x + 1);
}
int main(int argc, char **argv) {
rek(1);
}
I wrote a function using a recursion. While testing it, it turned out, that the function is killed without any obvious reason, while the recursion is still running.
To test this, I wrote an infinite recursion.
On my PC this function quits after about 2 seconds and the last output is about 327400.
The last number isn't always the same.
I am using Ubuntu Lucid Lynx, the GCC compiler and Eclipse as IDE. If somebody has an idea what the problem is and how I can prevent the program from exiting I would be really pleased.
#include <iostream>
void rek(double x){
std::cout << x << std::endl;
rek(x + 1);
}
int main(int argc, char **argv) {
rek(1);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
您很可能会溢出堆栈,此时您的程序将被立即终止。堆栈的深度始终会限制您可以递归的数量,如果达到该限制,则意味着您的算法需要更改。
You are most likely overflowing the stack, at which point your program will be summarily killed. The depth of the stack will always limit the amount you can recurse, and if you are hitting that limit, it means your algorithm needs to change.
我认为您期望代码永远运行是正确的,如
如何检查 gcc 是否正在执行尾递归优化?
如果 gcc 正在执行尾递归,您的代码应该能够永远运行。在我的机器上,看起来 -O3 实际上使 gcc 生成尾调用并实际上压平了堆栈。 :-)
我建议您将优化标志设置为 O2 或 O3。
I think you are right in expecting the code to run forever, as explained in
How do I check if gcc is performing tail-recursion optimization?
your code should be able to run for ever and ever, if gcc is performing tail recursion. On my machine it looks like -O3 actually makes gcc generate tail calls and actually flatten the stack. :-)
I surgest you set the optimize flag to O2 or O3.
由于您没有提供退出条件,因此导致堆栈溢出(堆栈空间不足)。
You are causing a stack overflow (running out of stack space) because you don't provide an exit condition.
你希望这永远有效吗?
不会的。在某些时候你会耗尽堆栈。
Are you expecting this to work forever?
It won't. At some point you're going to run out of stack.
这很有趣,在 stackoverflow.com 上谈论堆栈溢出。 ;) 调用堆栈是有限的(您可以从项目设置中自定义它),但在某些时候,当您有无限循环调用时,它将超出并且您的程序终止。
This is funny, talking about stack overflow on stackoverflow.com. ;) The call stack is limited (you can customized it from the project settings), but at some point, when you have infinite loop calls, it will be exceed and your program terminated.
如果您想避免无限递归的堆栈溢出,那么不幸的是,您将不得不深入研究某些程序集以更改堆栈,以便新的激活记录不会不断地推入堆栈,这在某些时候会导致溢出。因为您在函数末尾进行递归调用,所以在递归流行的其他语言(即 Lisp、Scheme、Haskell 等)中,这被称为跟踪调用优化。它基本上通过将尾部调用转换为循环来防止堆栈溢出。在 C 中它会是这样的(注意:我在 x86 上使用带有 gcc 的内联汇编,并且我将你的参数从
double
更改为int
以简化另外,我已从 C++ 更改为 C,以避免函数名称的名称修改。最后,每个语句末尾的“\n\t”不是实际的汇编命令,而是内联汇编所需要的。 gcc):在 Ubuntu 10.04 上使用 gcc 4.4.3 编译,它在无限循环中几乎“永远”运行,没有堆栈溢出,而如果没有尾部调用优化,它很快就会因分段错误而崩溃。从
__asm
部分的注释中可以看到堆栈激活记录空间是如何被“回收”的,以便每次新的调用都不会耗尽堆栈上的空间。这涉及到将键值保存在旧的激活记录中(前一个调用者的激活记录基指针和返回地址),并恢复它们,但参数会更改以供下一次递归调用该函数。同样,其他语言(主要是函数式语言)执行尾部调用优化作为该语言的基本功能。因此,Scheme/Lisp/etc 中的尾调用递归函数。不会溢出堆栈,因为当新函数调用作为现有函数的最后一个语句时,这种类型的堆栈操作是在幕后完成的。
If you want to avoid a stack overflow with infinite recursion, you're unfortunately going to have to delve into some assembly in order to change the stack so that a new activation record isn't constantly pushed onto the stack, which after some point will cause the overflow. Because you make the recursive call at the end of the function, this is called in other languages where recursion is popular (i.e., Lisp, Scheme, Haskell, etc.) a trail-call optimization. It prevents a stack overflow by basically transforming the tail-call into a loop. It would be something like this in C (note: I'm using inline assembly with gcc on x86, and I changed your arguments to
int
fromdouble
in order to simplify the assembly. Also I've changed to C from C++ in order to avoid name-mangling of function-names. Finally the "\n\t" at the end of each statement is not an actual assembly command but is needed for inline assembly in gcc):Compiled with gcc 4.4.3 on Ubuntu 10.04, this ran pretty much "forever" in an infinite loop with no stack overflow, where-as without the tail-call optimization, it crashed with a segmentation fault pretty quickly. You can see from the comments in the
__asm
section how the stack activation record space is being "recycled" so that each new call does not use up space on the stack. This involves saving the key values in the old activation record (the previous caller's activation record base pointer and the return address), and restoring them, but with the arguments changed for the next recursive call to the function.And again, other languages, mainly functional languages, perform tail-call optimization as a base-feature of the language. So a tail-call recursive function in Scheme/Lisp/etc. won't overflow the stack since this type of stack manipulation is done under-the-hood for you when a new function call is made as the last statement of an existing function.
好吧,您已经定义了无限递归和堆栈溢出,这会杀死您的应用程序。如果你真的想打印所有数字;然后使用循环。
Well you have defined infinite recursion and overflowing the stack, which kills your app. If you really want to print all numbers; then use a loop.
每个递归方法都应该实现一个退出条件,否则将会出现堆栈溢出并且程序将终止。
在您的情况下,您传递给函数的参数没有条件,因此,它会永远运行并最终崩溃。
Each recursive method should implement an exit condition, otherwise you will get stack overflow and the program will terminate.
In your case, there is no condition on the parameter you are passing to the function,hence, it runs forever and eventually crashes.