Segmentation fault(scanfで&を付け忘れる)
scanfは書き込み先のアドレスを受け取る関数です。scanf("%d", x)のように&を付け忘れると、変数の値そのものを不正なアドレスとして書き込もうとし、クラッシュします。
エラーメッセージの読み方
Segmentation fault
(エラーメッセージなし)- これはプログラム自身の出力ではありません。プログラムは何も言わずに落ちています
Segmentation fault- シェルが検知した異常終了の種類 — OSが「不正なメモリアクセス」などを検出しプロセスを強制終了させました
見つけ方- このメッセージだけでは行番号すら分かりません。原因はコードを読んで特定するしかありません
このエラーが出る典型パターン
パターン1
1 #include <stdio.h> 2 3 int main(void) { 4 int age; 5 sscanf("42", "%d", age ); ^ 6 printf("%d\n", age); 7 return 0; 8 }
scanf系の関数は書き込み先のアドレスを受け取ります。&を付け忘れると、変数の値そのものを不正なアドレスとして書き込もうとしクラッシュします。
直し方: age を &age にします。
パターン2
1 #include <stdio.h> 2 3 int main(void) { 4 int score; 5 sscanf("100", "%d", score ); ^ 6 printf("%d\n", score); 7 return 0; 8 }
printfは値を読むだけなので変数名で足りますが、書き込みを行うscanf系の関数にはアドレス(&変数名)が必要です。
直し方: score を &score にします。
パターン3
1 #include <stdio.h> 2 3 int main(void) { 4 int count; 5 sscanf("7", "%d", count ); ^ 6 printf("%d\n", count); 7 return 0; 8 }
この非対称性(読むだけのprintfと書き込むscanf系)がscanfの&忘れを起こしやすくしています。標準入力から読むscanf自体でも同じ問題が起こります。
直し方: count を &count にします。
パターン4
1 #include <stdio.h> 2 3 int main(void) { 4 int qty; 5 sscanf("15", "%d", qty ); ^ 6 printf("%d\n", qty); 7 return 0; 8 }
&を付け忘れると、変数の値そのものを不正なアドレスとして書き込もうとしクラッシュします。
直し方: qty を &qty にします。
パターン5
1 #include <stdio.h> 2 3 int main(void) { 4 int year; 5 sscanf("2026", "%d", year ); ^ 6 printf("%d\n", year); 7 return 0; 8 }
書き込みを行うscanf系の関数には、変数のアドレス(&変数名)を渡す必要があります。
直し方: year を &year にします。
よくある誤解
printfとscanfは似た形をしていますが役割が逆です。printfは値を「読む」ので変数名だけでよく、scanfは値を「書き込む」ので書き込み先のアドレス(&変数名)が必要です。この非対称性が付け忘れの原因になります。
実務での勘所
&を付け忘れると、scanfはxという「変数の値」を受け取ります。scanfは受け取った引数を常に「書き込み先のアドレス」だと信じて疑わないため、そのxの値(たとえば初期化されていないゴミの値や0)を、あたかも有効なメモリアドレスであるかのように扱って書き込もうとします。ほとんどの場合、そのアドレスはプログラムに割り当てられていない領域を指しているため、書き込んだ瞬間にセグメンテーション違反でクラッシュします。