0Pricing
C Academy · レッスン

スタックオーバーフローを防ぐ

再帰の深さを制限します。

「スタックオーバーフローを防ぐ」はCoddyKit上の無料C Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C Academyコースには全4レッスンが含まれています。

スタックオーバーフローとは

呼び出しスタックのサイズには上限があります。各関数呼び出しは、引数やローカル変数のために、その一部を使用します。

再帰が深くなりすぎるとスタックが埋まり、プログラムはスタックオーバーフローでクラッシュします。

基底ケースがない場合

最も一般的な原因は、決して到達しない基底ケースです。この状態では無限に呼び出しが続き、スタックオーバーフローが発生します。

このような関数は実行せず、なぜ失敗するのかを確認してください。

int broken(int n) {
    /* no base case: never stops */
    return broken(n + 1);
}

引数が小さくなっていない

基底ケースがあっても、引数はそこに近づいていかなければなりません。ここでは n が増加するため、0 に到達しません。

各呼び出しで、停止条件に近づいていることを必ず確認してください。

int oops(int n) {
    if (n == 0) return 0;
    return oops(n + 1); /* wrong direction */
}

正しいバージョン

変化の方向を正しくすると、処理が終了するようになります。ここでは n が基底ケースの 0 に向かって減少します。

#include <stdio.h>

int good(int n) {
    if (n == 0) return 0;
    return n + good(n - 1);
}

int main(void) {
    printf("%d\n", good(10));
    return 0;
}

深さの上限に注意する

正しい再帰でも、非常に深くなるとオーバーフローする可能性があります。何百万レベルも深く関数を呼び出すと、通常は数メガバイトしかないスタックを超えることがあります。

深さが非常に大きくなる場合は、反復を優先してください。

深い再帰をループに変換する

再帰の深さが入力サイズに応じて増える場合は、ループに切り替えてください。これにより、何千ものフレームが積み重なるのを防げます。

次のループは、一定のメモリ使用量で大きな n に対する 1 から n までの合計を安全に計算します。

#include <stdio.h>

int main(void) {
    long total = 0;
    for (int i = 1; i <= 1000000; i++)
        total += i;
    printf("%ld\n", total);
    return 0;
}

分割統治で深さを減らす

処理を半分に分けると、深さを小さく保てます。範囲を半分ずつに分けて合計すれば、深さの増加は線形ではなく、サイズの対数に比例します。

long range_sum(int lo, int hi) {
    if (lo == hi) return lo;
    int mid = (lo + hi) / 2;
    return range_sum(lo, mid) + range_sum(mid + 1, hi);
}

大きなローカル配列に注意する

大きなローカル変数があると各フレームが重くなり、スタックがより早く埋まります。

再帰関数内で大きな配列を宣言するのは避け、代わりにポインタを渡すかヒープを使ってください。

void heavy(int n) {
    int buffer[10000]; /* big frame each call */
    if (n == 0) return;
    heavy(n - 1);
}

アキュムレータを使う

途中までの合計をアキュムレータとして渡すと、各フレームを小さく保て、末尾再帰の形にできます。

その場合、一部のコンパイラは1つのフレームを再利用できます。

#include <stdio.h>

long sum_acc(int n, long acc) {
    if (n == 0) return acc;
    return sum_acc(n - 1, acc + n);
}

int main(void) {
    printf("%ld\n", sum_acc(100, 0));
    return 0;
}

安全性チェックリスト

再帰関数を信頼して使う前に、次を確認してください。

1. 基底ケースがありますか?
2. すべての呼び出しが基底ケースに近づいていますか?
3. 大きな入力で深さが非常に大きくなる可能性はありませんか?

深さが急増する可能性があるなら、代わりにループを使ってください。

小さな入力でテストする

再帰はまず、手計算で確認できる小さな入力を使って必ずテストしてください。

小さなケースで正常に動作し、深さも抑えられていれば、自信を持って規模を大きくできます。

理解度チェック

最も安全な修正方法を見つけてください。

まとめ

スタックオーバーフローは、再帰が深くなりすぎるか、終了しない場合に発生します。到達可能な基底ケースを必ず用意し、呼び出しごとに引数を小さくし、フレームを軽く保ち、入力サイズに応じて深さが増える場合は反復に切り替えてください。

よくある質問

「スタックオーバーフローを防ぐ」レッスンは無料ですか?

はい。「スタックオーバーフローを防ぐ」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C Academyコースには全4レッスンが含まれています。

「スタックオーバーフローを防ぐ」で何を学びますか?

再帰の深さを制限します。 ブラウザで直接実行するハンズオンコードでC Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

C Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのC Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「スタックオーバーフローを防ぐ」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このC Academyレッスンでコードを書いて実行できますか?

はい。すべてのC Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 再帰の仕組み
  2. 古典的な再帰問題
  3. 再帰と反復の比較
  4. スタックオーバーフローを防ぐ
← C Academyに戻る