広告スペース

第32回 実行時エラーとSegfault

C言語のSegmentation Faultの原因と対処法。実行時エラーを完全解説。

実行時エラーとは ― コンパイルは通るのに…

コンパイルが通る=文法は正しい。しかし実行してみると異常終了するのが実行時エラーです。コンパイラは教えてくれないため、デバッグが難しくなります。
Segmentation Fault
アクセス禁止のメモリを読み書きした。最も頻出の実行時エラー。
Bus Error
メモリのアライメント違反など。環境依存で発生。
Floating Point Exception
ゼロ除算(x / 0)で発生。
無限ループ・フリーズ
プログラムが止まらない。Ctrl+Cで強制終了。
実行時エラーの共通対処法:①printfで直前まで動いているか確認 → ②怪しい変数の値を表示 → ③配列の添字やポインタを重点チェック

Segmentation Fault ― 原因ランキング

Segmentation fault (core dumped) はC言語初心者が最も遭遇するエラーです。「触ってはいけないメモリに触った」という意味です。

原因Top5

1
配列の範囲外アクセス
int a[5];
a[10] = 99;  // a[0]〜a[4]しかないのにa[10]に書き込み→即死

// よくあるパターン: ループの境界ミス
for (int i = 0; i <= 5; i++)  // i=5でa[5]にアクセス→NG
  a[i] = i;
2
NULLポインタの参照
int *p = NULL;
*p = 10;  // NULLが指すアドレスに書き込み→Segfault

// 対策: 使う前にNULLチェック
if (p != NULL) { *p = 10; }
3
scanf の & 忘れ
int x;
scanf("%d", x);   // NG: xの「値」をアドレスとして使う→暴走
scanf("%d", &x);  // OK: xの「アドレス」を渡す
4
未初期化ポインタの使用
int *p;     // 初期化していない→ゴミアドレスが入っている
*p = 42;    // ゴミアドレスに書き込み→Segfault

// 対策: 必ず初期化するか、mallocで確保してから使う
5
スタックオーバーフロー(無限再帰)
int fact(int n) {
  return n * fact(n - 1); // 終了条件がない→永遠に呼び続ける→スタック溢れ
}
// 対策: if (n <= 1) return 1; を追加

メモリ系エラー ― 見えにくい地雷

Segfaultにならなくても、メモリ関連のバグは結果がおかしくなる形で現れることがあります。
バッファオーバーラン
配列のサイズを超えて書き込むと、隣のメモリを破壊する。
char name[5];
strcpy(name, "HelloWorld"); // 5バイトしかないのに10文字+\0→溢れる

// 対策: strncpy(name, "Hello", sizeof(name)-1);
メモリリーク
mallocで確保したメモリをfreeし忘れると、使えないメモリが溜まり続ける。
int *p = malloc(sizeof(int) * 100);
// ... pを使う ...
// free(p); を忘れた → メモリリーク
ダングリングポインタ(二重free)
freeした後のポインタを使い続けると、予測不能な動作になる。
free(p);
*p = 10;   // 解放済みメモリに書き込み→未定義動作
free(p);   // 二重free→クラッシュの可能性

// 対策: free後に p = NULL; とする

論理エラー ― 動くけど答えが違う

クラッシュはしないが結果が期待と違う。最もデバッグが難しいタイプです。printfデバッグが威力を発揮します。
整数除算の罠
int a = 7, b = 2;
double avg = a / b;     // → 3.000000(3.5にならない!)
double avg = (double)a / b; // → 3.500000(OK)
int同士の割り算は小数点以下が切り捨てられる。片方をdoubleにキャスト。
演算子の優先順位
if (x & 1 == 0)   // → x & (1==0) と解釈される(意図と違う!)
if ((x & 1) == 0) // → OK: カッコで明示
ビット演算と比較演算の優先順位は直感と逆。迷ったらカッコ。
switch の break 忘れ(フォールスルー)
switch (n) {
  case 1: printf("一\n");  // break無し→case 2もcase 3も実行される
  case 2: printf("二\n");
  case 3: printf("三\n");
}
if文のセミコロン
if (x > 0);  // ← この ; で if文が終了してしまう
{
  printf("正の数\n"); // ← 常に実行される!
}

トラブルシューティング・チェックリスト

エラーに遭遇したとき、このチェックリストを上から順に確認してみましょう。

Segmentation Fault が出たら

結果がおかしいとき

コンパイルエラーが大量に出たら

広告スペース

関連する講座

リファレンス編
第31回 コンパイルエラー辞典
C言語のコンパイルエラー一覧と対処法。gccのエラーメッセージの読み方を完全解説。
繰り返し・配列・文字列
第15回 デバッグの技法
C言語のデバッグ方法。printfデバッグとよくあるバグの見つけ方を実践的に解説。
発展編
第25回 ポインタの基礎
C言語のポインタをメモリ可視化で理解。アドレスと間接参照を図解。
発展編
第28回 動的メモリ(malloc/free)
C言語の動的メモリ確保。malloc, free, メモリリークの原因と対策。
← 前の講座
第31回 コンパイルエラー辞典

よくある質問(FAQ)

Q. コンパイルエラーと実行時エラーの違いは?

A. コンパイルエラーはコンパイル中に検出される文法的な問題で、実行ファイルが作られません。実行時エラーはプログラムが実行中に発生する問題で、コンパイルは通りますがプログラムが強制終了します。実行時エラーの方が検出が難しいのが特徴です。

Q. Segmentation Faultが出ました。何が原因?

A. セグメンテーション フォルト(Segfault)は不正なメモリアクセスが原因です。主な原因は:1. NULLポインタへのアクセス2. 解放済みメモリへのアクセス3. 配列の範囲外アクセス4. スタックオーバーフローです。ポインタやメモリ管理を見直しましょう。

Q. 無限ループをデバッグするには?

A. 1. ループ条件を確認し、終了条件が正しいか見直す。2. printf()で変数値を出力して、どこで値が想定と異なるかを追跡する。3. ステップ実行でデバッガを使うのも効果的です。無限ループの多くはループ条件やインクリメント処理の誤りが原因です。

Q. メモリ破壊はどう発見する?

A. メモリ破壊は症状が遠く離れた場所に現れるため、発見が難しいです。有効な手段は:1. valgrindツールを使う(Linux/Mac)、2. Ubsan/Asanなどのサニタイザーを有効にする、3. 配列やポインタの使用を慎重に見直すことです。

確認クイズ

この講座の理解度をチェックしましょう!

Q1. セグメンテーションフォルトの主な原因は?

構文エラー
不正なメモリアクセス
変数名の間違い

NULLポインタの参照、配列の範囲外アクセス、解放済みメモリの使用などが原因です。コンパイル時には検出されません。

Q2. 0 で割り算するとどうなる?

コンパイルエラー
実行時エラー(ゼロ除算)
結果が 0 になる

整数の 0 除算は実行時エラーになります。変数で割る場合は事前に 0 でないかチェックしましょう。

Q3. 無限ループの対処法は?

コンパイラの設定を変える
ループの終了条件を見直す
変数の型を変える

無限ループは終了条件が満たされないために発生します。カウンタ変数の更新忘れや条件式の間違いを確認しましょう。

この記事をシェア
X(Twitter)でシェア Facebookでシェア LINEで送る はてブ

この講座の理解を深めるおすすめ書籍

サイトで動きを理解し、書籍で演習量を補うと効果的です

📘
苦しんで覚えるC言語
MMGames 著
初心者向けの定番入門書。丁寧な解説で基礎を固められます。
Amazonで見る
📗
新・明解C言語 入門編
柴田望洋 著
図解が豊富で、演習問題も充実。大学の教科書としても採用多数。
Amazonで見る
📙
プログラミング言語C 第2版
B.W.カーニハン, D.M.リッチー 著
通称K&R。C言語の原典。基礎を終えた後のステップアップに最適。
Amazonで見る

※ 上記リンクはアフィリエイトリンクです。購入によりサイト運営を支援いただけます。