简体   繁体   English

递归函数引起的堆栈溢出

[英]Stack overflow caused by recursive function

I'm a beginner in C++.我是 C++ 的初学者。 Yesterday I read about recursive functions, so I decided to write my own.昨天我读了递归函数,所以我决定自己写。 Here's what I wrote:这是我写的:

int returnZero(int anyNumber) {
    if(anyNumber == 0)
        return 0;
    else  {
        anyNumber--;
        return returnZero(anyNumber);
    }
}

When I do this: int zero1 = returnZero(4793);当我这样做时: int zero1 = returnZero(4793); , it causes a stack overflow. ,它会导致堆栈溢出。 However, if I pass the value 4792 as the argument, no overflow occurs.但是,如果我将值 4792 作为参数传递,则不会发生溢出。

Any ideas as to why?关于为什么的任何想法?

Whenever you call a function, including recursively, the return address and often the arguments are pushed onto the call stack .每当您调用函数时,包括递归调用,返回地址和参数通常会被推送到调用堆栈上 The stack is finite, so if the recursion is too deep you'll eventually run out of stack space.堆栈是有限的,因此如果递归太深,您最终会耗尽堆栈空间。

What surprises me is that it only takes 4793 calls on your machine to overflow the stack.让我感到惊讶的是,在您的机器上只需要 4793 次调用即可溢出堆栈。 This is a pretty small stack.这是一个非常小的堆栈。 By way of comparison, running the same code on my computer requires ~100x as many calls before the program crashes.相比之下,在我的计算机上运行相同的代码需要大约 100 倍的调用次数才能让程序崩溃。

The size of the stack is configurable.堆栈的大小是可配置的。 On Unix, the command is ulimit -s .在 Unix 上,命令是ulimit -s

Given that the function is tail-recursive , some compilers might be able to optimize the recursive call away by turning it into a jump.鉴于该函数是tail-recursive ,一些编译器可能能够通过将其转换为跳转来优化递归调用。 Some compilers might take your example even further: when asked for maximum optimizations, gcc 4.7.2 transforms the entire function into:一些编译器可能会更进一步:当被要求进行最大优化时, gcc 4.7.2将整个函数转换为:

int returnZero(int anyNumber) {
  return 0;
}

This requires exactly two assembly instructions:这正好需要两条汇编指令:

_returnZero:
        xorl    %eax, %eax
        ret

Pretty neat.漂亮整齐。

You just hit the call stack's size limit of your system, that's what's happening.您刚刚达到了系统调用堆栈的大小限制,这就是正在发生的事情。 For some reason the stack in your system is tiny, a depth of 4793 function calls is rather small.由于某种原因,您系统中的堆栈很小,4793 个函数调用的深度相当小。

Your stack is limited in size and so when you make 4793 calls you are hitting the limit while 4792 just comes in under.您的堆栈大小有限,因此当您进行4793调用时,您正在达到限制,而4792刚刚进入。 Each function call will use some space on the stack for house keeping and maybe arguments.每个函数调用都会使用堆栈上的一些空间来进行内部管理,也可能是参数。

This page gives an example of what a stack looks like during a recursive function call.此页面提供了一个示例,说明在递归函数调用期间堆栈的外观。

My guess is you stack is exactly big enough to fit 4792 entries - today.我的猜测是您的堆栈完全足够容纳 4792 个条目 - 今天。 Tomorrow or the next, that number might be different.明天或下个月,这个数字可能会有所不同。 Recursive programming can be dangerous and this example illistrates why.递归编程可能很危险,这个例子说明了原因。 We try not to let recursion get this deep or 'bad' things can happen.我们尽量不让递归变得如此深入或“坏”的事情可能发生。

Any "boundless" recursion, that is recursive calls that aren't naturally limited to a small(ish) number will have this effect.任何“无限”递归,即自然不限于小(ish)数的递归调用都会产生这种效果。 Exactly where the limit goes depends on the OS, the environment the function is called in (the compiler, which function calls the recursive function, etc, etc).限制的确切位置取决于操作系统、调用函数的环境(编译器、哪个函数调用递归函数等)。

If you add another variable, say int x[10];如果添加另一个变量,请说int x[10]; to your function that calls your recursive function, the number needed to crash it will change (probably by about 5 or so).对于调用递归函数的函数,使其崩溃所需的数量会改变(可能大约 5 左右)。

Compile it with a different compiler (or even different compiler settings, eg optimization turned on) and it will probably change again.使用不同的编译器(甚至不同的编译器设置,例如打开优化)编译它,它可能会再次更改。

Using recursion, you can achieve SuperDigit:使用递归,你可以实现 SuperDigit:

public class SuperDigit
{
    static int sum = 0;

    int main()
    {
        int n = 8596854;
        cout<<getSum(n);
    }

    int getSum(int n){
        sum=0;
        while (n > 0) {
            int rem;
            rem = n % 10;
            sum = sum + rem;
            n = n / 10;
            getSum(n);
        }
        return sum;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM