Code Fix

初級

Segmentation fault(NULLポインタの参照)

NULL(何も指していないポインタ)の指す先を読み書きしようとしたときに発生するクラッシュです。Cで最も頻出する実行時エラーです。

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

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

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

パターン1

 1  #include <stdio.h>
 2  #include <stdlib.h>
 3  
 4  int main(void) {
 5      int *p = NULL               ;
                          ^
 6      printf("%d\n", *p);
 7      return 0;
 8  }
Segmentation fault

NULLは「何も指していない」という印です。*pでその指す先を読もうとした瞬間、無効なアドレスへのアクセスとしてクラッシュします。

直し方: NULL を malloc(sizeof(int)) にします。

この問題を解いてみる →

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

パターン2

 1  #include <stdio.h>
 2  #include <stdlib.h>
 3  
 4  int main(void) {
 5      int value = 7;
 6      int *p = NULL  ;
                    ^
 7      *p = value;
 8      printf("%d\n", *p);
 9      return 0;
10  }
Segmentation fault

pがNULLのまま*p = valueで書き込もうとすると、読み込みと同様にクラッシュします。有効な変数のアドレス(&value)を指すようにする必要があります。

直し方: NULL を &value にします。

この問題を解いてみる →

パターン3

 1  #include <stdio.h>
 2  #include <stdlib.h>
 3  
 4  int main(void) {
 5      int *p = malloc(sizeof(int));
 6      p = NULL     ;
                ^
 7      *p = 1;
 8      printf("%d\n", *p);
 9      return 0;
10  }
Segmentation fault

せっかくmallocで有効な領域を確保していても、途中でpにNULLを代入し直すと、その後の*pは無効な参照になります。

直し方: NULL を p にします。

この問題を解いてみる →

パターン4

 1  #include <stdio.h>
 2  #include <stdlib.h>
 3  
 4  int main(void) {
 5      int total = 0;
 6      int *ptr = NULL               ;
                            ^
 7      total += *ptr;
 8      printf("%d\n", total);
 9      return 0;
10  }
Segmentation fault

ptrがNULLのまま*ptrを読み取ろうとするとクラッシュします。

直し方: NULL を malloc(sizeof(int)) にします。

この問題を解いてみる →

パターン5

 1  #include <stdio.h>
 2  #include <stdlib.h>
 3  
 4  int main(void) {
 5      int *count = NULL               ;
                              ^
 6      *count = 10;
 7      printf("%d\n", *count);
 8      return 0;
 9  }
Segmentation fault

書き込み先がNULLのままだと、代入しようとした瞬間にクラッシュします。

直し方: NULL を malloc(sizeof(int)) にします。

この問題を解いてみる →

よくある誤解

NULLを代入した行では何も起きません。実際にそのポインタを*pや->で参照した瞬間にクラッシュします。関数がNULLを返す可能性がある場合(mallocの失敗など)、使う前にNULLチェックをしないと危険です。

実務での勘所

NULLポインタの参照が確実にクラッシュする理由は偶然ではありません。多くのOSは、アドレス0を含む先頭のメモリページを意図的に「どのプロセスにも割り当てない」ように設定しています。NULL(通常はアドレス0)を参照しようとすると、CPUのメモリ管理機構がこの未割り当て領域へのアクセスを検出してハードウェア例外を発生させ、OSがSIGSEGV(セグメンテーション違反)としてプロセスを止めます。これは事故を確実に検出できるようにするための意図的な安全装置で、他の任意の不正なアドレスよりもNULL参照のほうが確実にクラッシュしてくれるのはこのためです。

演習をはじめる

関連するエラー

他の言語ではどうなるか

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