セグメンテーション違反とは?
セグメンテーション違反(segmentation fault、segfault)とは、プログラムがアクセスを許されていないメモリを読み書きしようとしたときに起きるクラッシュです。ヌルポインタを通してアドレス0を読むのが典型例で、オペレーティングシステムが SIGSEGV シグナルでプログラムを停止させます。
更新日: 2026年9月24日
Cのプログラムが最初の行を表示した後、誰も書いた覚えのないメッセージを出して止まります。
#include <stdio.h>
int main(void) {
int *score = NULL;
printf("About to read the score\n");
printf("Score: %d\n", *score);
return 0;
}
About to read the score
bash: line 1: 64030 Segmentation fault: 11 ./seg
この出力はmacOSのbashのもので、64030はプロセスID、11はシグナル番号です。Linuxでは、同じクラッシュはたいてい Segmentation fault (core dumped) と表示されます。どちらの場合も、プログラムはスコアを表示していません。score にはアドレス0が入っていて、*score はそのアドレスのメモリを読むようプロセッサに求めますが、それはどのプログラムにも許されていないのです。
セグメンテーション違反はどのように起きるのか
どのプログラムも、それぞれ専用の仮想アドレス空間の中で動いています。これは巨大なアドレスの範囲で、オペレーティングシステムが少しずつ中身を割り当てていきます。OSは、プログラムのコード、グローバル変数、スタック、ヒープを、ページと呼ばれるブロック単位でこの範囲に対応付けます(ほとんどのx86のLinuxシステムでは4 KB、Appleシリコン搭載のMacでは16 KB)。ほとんどのアドレスは対応付けられないまま残され、ヌルポインタのバグを捕まえられるよう、アドレス0は必ずその中に含まれます。
- プログラムが、あるアドレスを読み書きする命令を実行します。ここでは
*scoreの読み取り、つまりアドレス0です。 - プロセッサのメモリ管理ユニットが、ページテーブルでそのアドレスを調べます。そのページは対応付けられていないか、プログラムが読み取り専用のページに書き込もうとしています。
- プロセッサは命令を止め、ページフォールトとしてカーネルに制御を渡します。
- カーネルは、そのアクセスが正当なものになり得るか(たとえば、スタックを拡張する必要があるだけか)を確認します。正当ではないので、カーネルはプロセスにシグナル11、
SIGSEGVを送ります。 SIGSEGVのデフォルトの動作は、プロセスを終了させ、システムが許可していればコアダンプを保存することです。その後シェルがメッセージを表示し、終了ステータスを139(128 + シグナル番号)に設定します。
つまりセグメンテーション違反はコンパイラが報告するものではなく、言語が発生させる例外でもありません。ハードウェアとカーネルがメモリを保護しているのであり、もっとも突然に起きる種類の実行時エラーです。Windowsは同じ現象を、例外コード 0xC0000005 のアクセス違反として扱います。
セグメンテーション違反のよくある原因
CとC++では、プログラムがどんなアドレスでも計算して使えるので、原因は結局、有効でないアドレスを使うことに行き着きます。
- ヌルポインタの参照外し。
int *p = NULL; *p = 5;のようなコードです。mallocやfopenのように失敗するとNULLを返す関数は、結果をチェックしないとここにつながります。 - 配列の末尾をはるかに超えたインデックス。 Cは境界をチェックしないので、
arr[1000000]は単に100万要素先のアドレスになります。 freeした後のメモリの使用。 ポインタには古いアドレスがまだ入っていますが、そのメモリはもうあなたのものではありません。- 初期化されていないポインタ。
int *p; *p = 5;は、pにたまたま入っているでたらめな値を通して書き込みます。 - スタックオーバーフロー。 停止条件のない再帰関数は、スタックの末端を越えるまでスタックフレームを積み続けます。次の例は、macOSで
Segmentation fault: 11と終了ステータス139でクラッシュしました。 - 文字列リテラルへの書き込み。
char *name = "coddy"; name[0] = 'C';は読み取り専用のメモリを変更しようとします。Linuxではこれはセグメンテーション違反になります。macOSでは、同じプログラムが関連するシグナルであるBus error: 10で停止しました。
#include <stdio.h>
int depth(int n) {
return depth(n + 1) + 1; /* never stops calling itself */
}
int main(void) {
printf("%d\n", depth(0));
return 0;
}
小さな誤りは、まったくクラッシュしないことがよくあり、そのほうが危険です。次のループは、要素が3つの配列の末尾を1つ超えて読み取ります。
#include <stdio.h>
int main(void) {
int scores[3] = {72, 88, 95};
int total = 0;
for (int i = 0; i <= 3; i++) { /* <= reads scores[3] */
total += scores[i];
}
printf("Total: %d\n", total);
return 0;
}
MacのClangでコンパイルすると、Total: 256 と表示されました。正しい合計は255です。scores[3] はスタック上の次の4バイトで、それはプログラム自身のものだったので、フォールトは起きず、でたらめな値が黙って足されました。オペレーティングシステムが止めるのは、プログラムがまったく所有していないメモリへのアクセスだけなのです。
どちらの誤りも、直し方は同じ習慣です。要素がいくつあるかを把握し、ポインタをたどる前にチェックすることです。
Best: 95
No scores, nothing to read
「core dumped」の意味
コアダンプとは、クラッシュした瞬間のプログラムのメモリのコピーを保存したファイルです。後でデバッガで開くと(gdb ./app core)、プログラムがどこにいたのか、変数に何が入っていたのかを正確に確認できます。多くのLinuxディストリビューションでは、systemd-coredumpがこれらのファイルを集めていて、coredumpctl list で一覧を見られます。たとえば ulimit -c 0 でコアダンプを無効にしていると、メッセージは括弧の中の言葉がない Segmentation fault だけになります。
クラッシュした行を見つける方法
プログラムがクラッシュした行が、バグのある行とは限りません。ポインタはある関数の中で無効になり、ずっと後に別の関数で使われることがあります。次のツールを使えば、その両方が分かります。
デバッガ。 デバッグ情報付きでコンパイルし、デバッガの中でプログラムを実行します。停止したら、bt(バックトレース)で関数呼び出しの連鎖がファイル名と行番号付きで表示されます。Linuxではたいてい gdb を使い、macOSでは lldb を使います。lldb でも bt は同じように動きます。
gcc -g app.c -o app
gdb ./app
(gdb) run
(gdb) bt
AddressSanitizer。 -fsanitize=address を付けてコンパイルし(GCCとClangの両方が対応しています)、プログラムを普通に実行します。するとただのセグメンテーション違反の代わりに、heap-use-after-free や stack-buffer-overflow のように誤りの種類を名指しし、アクセスした行とメモリを確保した行を示すレポートが表示されます。上で見た、それ自体では決してクラッシュしない、末尾を1つ超えた静かな読み取りも捕まえてくれます。
Valgrind。 Linuxでは、valgrind ./app で手を加えていないプログラムを実行し、Invalid read of size 4 のような不正な読み書きをすべて報告させられます。
ほかの言語のセグメンテーション違反
Python、Java、JavaScriptは、すべてのインデックスとすべての参照を使う前にチェックするので、同じ誤りは分かりやすいメッセージ付きの例外になります。Pythonなら IndexError や AttributeError、Javaなら ArrayIndexOutOfBoundsException や NullPointerException、JavaScriptなら TypeError です。これらは例外処理で捕捉できます。セグメンテーション違反はそうはいきません。これはシグナルであり、C++の catch ブロックからは見えないのです。
それでも、下で動いているCのコードが失敗すれば、Pythonのプログラムもセグメンテーション違反を起こすことがあります。次の行は ctypes にアドレス0を読むよう求めています。
import ctypes
ctypes.string_at(0)
python3 -X faulthandler で実行すると、Python 3.12は Fatal Python error: Segmentation fault と表示し、続けて実行中だったPythonの行を表示しました。C拡張やネイティブライブラリにメモリのバグがある場合も、同じクラッシュが起きます。Rustは別の方法を取っていて、無効なメモリにアクセスする可能性のあるコードのほとんどを、プログラムがビルドされる前にコンパイラが拒否します。
次に読むページ
Cのセグメンテーション違反のガイドでは、それぞれの原因を最小限のプログラムとその修正方法で順に説明しています。そもそも誤りを避けるには、ポインタ、ヌルポインタ、スタックとヒープについて読み、Cコースで練習してください。メモリをチェックしてくれる言語でエラーがどのように報告されるかは、実行時エラーのページを参照してください。
よくある質問
セグメンテーション違反はどうやって直せばいいですか?
-g を付けてコンパイルし、プログラムを gdb か lldb の下で実行して、クラッシュ後に bt と入力します。詳しいレポートがほしければ -fsanitize=address を付けてコンパイルします。次に、その行が使っているポインタやインデックスを直します。ポインタが NULL でないかをチェックする、配列のインデックスを長さ未満に収める、free した後のメモリを使わない、再帰が必ず終わるようにする、といった対処です。セグメンテーション違反とメモリリークは同じものですか?
free の誤りはセグメンテーション違反の原因になり、free の書き忘れはリークの原因になります。なぜ「セグメンテーション違反」という名前なのですか?
SIGSEGV は残りました。Windowsでは同じ現象をアクセス違反と呼びます。Pythonでもセグメンテーション違反は起きますか?
ctypes などです。python -X faulthandler script.py で実行すると、クラッシュしたときに実行中だったPythonの行が表示されます。終了コード139はどういう意味ですか?
SIGSEGV によって強制終了されたという意味です。シェルは、シグナルによる終了を「128 + シグナル番号」として報告し、128 + 11 = 139 になります。DockerやKubernetesでコンテナが139で終了した場合、そのメインプロセスがセグメンテーション違反を起こしています。