web-dev-qa-db-ja.com

再帰関数によるスタックオーバーフロー

C++プログラミングとコンピュータシステムアーキテクチャの初心者である私は、まだC++の基礎を学んでいます。昨日再帰関数について読んだので、自分で書くことにしました。これが私が書いたものです:(非常に基本的な)

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

}

そして私がこれを行うとき:int zero1 = returnZero(4793);スタックオーバーフローが発生しますが、値4792をパラメータとして渡しても、オーバーフローは発生しません。

理由について何かアイデアはありますか?

8
charles

再帰的に含む関数を呼び出すときはいつでも、戻りアドレスと多くの場合引数が 呼び出しスタック にプッシュされます。スタックは有限であるため、再帰が深すぎると、最終的にスタックスペースが不足します。

私が驚いたのは、スタックをオーバーフローさせるのにマシンで4793回の呼び出ししかかからないことです。これはかなり小さなスタックです。比較として、私のコンピューターで同じコードを実行するには、プログラムがクラッシュするまでに最大100倍の呼び出しが必要です。

スタックのサイズは構成可能です。 Unixでは、コマンドはulimit -sです。

関数が tail-recursive であるとすると、一部のコンパイラーは、再帰呼び出しをジャンプに変えることで、再帰呼び出しを最適化できる可能性があります。一部のコンパイラは、例をさらに詳しく説明します。最大の最適化を求められると、gcc 4.7.2は関数全体を次のように変換します。

int returnZero(int anyNumber) {
  return 0;
}

これには、正確に2つのアセンブリ命令が必要です。

_returnZero:
        xorl    %eax, %eax
        ret

かなりきちんと。

26
NPE

システムのコールスタックのサイズ制限に達しただけです。それが起こっていることです。何らかの理由で、システムのスタックは小さく、4793関数呼び出しの深さはかなり小さいです。

2
Óscar López

スタックのサイズには制限があるため、4793の呼び出しを行うと、4792がちょうど下にある間に制限に達します。各関数呼び出しは、ハウスキーピングとおそらく引数のためにスタック上のスペースを使用します。

このページでは、再帰的な関数呼び出し中にスタックがどのように見えるかを 示します。

2
Shafik Yaghmour

「無限の」再帰、つまり自然に小さい(ish)数に制限されない再帰呼び出しは、この効果があります。制限がどこに行くかは、OS、関数が呼び出される環境(コンパイラ、再帰関数を呼び出す関数など)によって異なります。

再帰関数を呼び出す関数にint x[10];などの別の変数を追加すると、それをクラッシュさせるのに必要な数が変わります(おそらく約5程度)。

別のコンパイラ(または最適化がオンになっているなど、別のコンパイラ設定)でコンパイルすると、おそらく再び変更されます。

1
Mats Petersson

私の推測では、スタックは4792エントリを収めるのに十分な大きさです-今日。明日か次の日か、その数は違うかもしれません。再帰的プログラミングは危険な場合があり、この例はその理由を示しています。再帰がこれほど深くならないようにします。そうしないと、「悪い」ことが起こる可能性があります。

1
Michael Dorgan

再帰を使用すると、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;
    }
}
0
Sufiyan Ansari