Code Fix

上級

Segmentation fault(再帰関数の終了条件忘れによるスタックオーバーフロー)

再帰関数に正しい終了条件(ベースケース)が無いと、関数呼び出しのたびにスタック領域が積み上がり続け、上限に達した時点でクラッシュします。

エラーメッセージの読み方

Segmentation fault
(エラーメッセージなし)
これはプログラム自身の出力ではありません。プログラムは何も言わずに落ちています
Segmentation fault
シェルが検知した異常終了の種類 — OSが「不正なメモリアクセス」などを検出しプロセスを強制終了させました
見つけ方
このメッセージだけでは行番号すら分かりません。原因はコードを読んで特定するしかありません

このエラーが出る典型パターン

パターン1

 1  #include <stdio.h>
 2  
 3  int factorial(int n) {
 4      if (0       ) {
                ^
 5          return 1;
 6      }
 7      return n * factorial(n - 1);
 8  }
 9  
10  int main(void) {
11      printf("%d\n", factorial(5));
12      return 0;
13  }
Segmentation fault

if (0)は常に偽なので、終了条件が一度も成立せずn-1、n-2……と再帰が終わらなくなります。関数呼び出しのたびにスタックが積み上がり、上限に達するとクラッシュします。

直し方: 0n <= 1 にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  #include <stdio.h>
 2  
 3  int sumTo(int n) {
 4      if (0       ) {
                ^
 5          return 0;
 6      }
 7      return n + sumTo(n - 1);
 8  }
 9  
10  int main(void) {
11      printf("%d\n", sumTo(5));
12      return 0;
13  }
Segmentation fault

ベースケースの条件を書き忘れる(あるいは常に偽になる条件を書いてしまう)と、再帰は自然には止まりません。

直し方: 0n <= 0 にします。

この問題を解いてみる →

パターン3

 1  #include <stdio.h>
 2  
 3  int power(int base, int exp) {
 4      if (0         ) {
                 ^
 5          return 1;
 6      }
 7      return base * power(base, exp - 1);
 8  }
 9  
10  int main(void) {
11      printf("%d\n", power(2, 4));
12      return 0;
13  }
Segmentation fault

呼び出しが数千〜数万回に達するまで正常に動いているように見えることもありますが、スタック領域はヒープよりずっと小さいため、いずれ必ず上限に達してクラッシュします。

直し方: 0exp <= 0 にします。

この問題を解いてみる →

よくある誤解

無限ループと違ってすぐには気づきにくいのが特徴です。呼び出しが数千〜数万回に達するまで正常に動いているように見えることもありますが、スタック領域はヒープよりずっと小さいため、いずれ必ず上限に達します。

まとめ

Segmentation fault(再帰関数の終了条件忘れによるスタックオーバーフロー)は上級でつまずきやすい項目です。上の3パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)