トラック4・🚪はじめてのC

scanfの罠——入力の恐怖を越える

読む目安 約14分・ゴール:scanfの3大トラップ(&忘れ・型違い入力・改行残り)の症状と対処を説明できる

アプリでこのトピックを学ぶ(無料・登録不要)

このトピックの要点

Cの入門でいちばん多く聞く悲鳴が、scanf まわりです。「実行した瞬間に落ちた」「何も入力していないのに勝手に進んだ」「同じ質問が無限に出続ける」。どれも原因ははっきりしていて、対処も決まっています。ここを越えれば、入力は怖くなくなります。

罠1——& を忘れる

#include <stdio.h>

int main(void) {
    int n;
    scanf("%d", n);   // & が無い!
    printf("%d\n", n);
    return 0;
}

症状は、実行した瞬間の異常終了(Windowsなら「動作を停止しました」、Linuxなら Segmentation fault)。scanf は「n の中に入っている値」を番地だと思い込み、まったく無関係な場所へ書き込もうとします。多くのコンパイラは warning: format '%d' expects argument of type 'int *' と教えてくれます。警告を読む習慣が、そのまま防具になります。

罠2——%d に文字を入れられる

数を聞いているのに abc と入力されたとき、何が起きるか。scanf は「数として読めない」と判断して何も読まずに戻ります。このとき abc は入力バッファに残ったままです。もう一度同じ scanf を実行しても、また同じ文字が残っていて読めない——これが「同じ質問が無限に出続ける」の正体です。

#include <stdio.h>

int main(void) {
    int n;
    int r = scanf("%d", &n);
    if (r != 1) {
        printf("数字ではありませんでした\n");
        int ch;
        while ((ch = getchar()) != '\n' && ch != EOF) {
            // 行末まで読み捨てる
        }
    }
    return 0;
}

対処は2段構えです。まず戻り値を必ず見る。そして読めなかったら、getcharで改行まで1文字ずつ読み捨てて、バッファを空にします。この「掃除」をしないと、次の入力もすべて道連れになります。

罠3——改行が残る(%c の事件)

#include <stdio.h>

int main(void) {
    int nen;
    char kigou;
    scanf("%d", &nen);
    scanf("%c", &kigou);   // ここで改行を拾ってしまう
    printf("[%d] [%c]\n", nen, kigou);
    return 0;
}

2026 と入力して Enter を押すと、バッファには 2026 と改行が入ります。%d は数字部分だけを読み、改行は残します。次の %c は「1文字」を素直に読むので、残っていた改行を掴んでしまう。ユーザーからは「文字を入力する前に勝手に進んだ」ように見えます。

対処は、%c の書式の前に半角スペースを1つ入れることです。

    scanf(" %c", &kigou);   // 先頭の空白が「空白類を読み飛ばせ」の指示

書式の中の空白は「空白・タブ・改行を読み飛ばせ」という命令になります。%d や %lf は自動で読み飛ばすので、この対策が要るのは %c と %[ のときだけです。

師範入力の恐怖の正体は、たった一つ。「バッファに何が残っているか」を見ていないことじゃ。読んだ量と残った量を数えられる者は、二度と怯えぬ。

まとめ

& を付けたか、戻り値を見たか、バッファに何が残っているか。この3つを確認する癖がつけば、scanfの事故はほぼ全部防げます。%c の前には空白を1つ。読めなかった行は getchar で掃除する。この2つは定型文として覚えてしまって構いません。

アプリでこのトピックを学ぶ(無料・登録不要)

確認問題

確認問題 1

キーボードから 12 と打って Enter を押しました(入力は数字2文字と改行1文字)。トレース表の空欄を埋めてください。まだ値が決まっていない欄は - と書きます。

#include <stdio.h>

int main(void) {
    int n;
    char c;
    int r1 = scanf("%d", &n);
    int r2 = scanf("%c", &c);
    printf("n=%d r1=%d r2=%d\n", n, r1, r2);
    return 0;
}
行nr1r2
6?1-
7121?
81211
答えを見る

空欄の答え(上から順に):12、1

6行目の %d は数字の 12 だけを読み、Enterの改行はバッファに残ります。7行目の %c は「1文字」を素直に読むので、残っていた改行を掴んでしまい、r2 は 1(1個読めた)になります。ユーザーからは「文字を入力する前に勝手に進んだ」ように見える、定番の事故です。対策は scanf(" %c", &c); と書式の先頭に半角スペースを1つ入れること。空白類を読み飛ばしてくれます。

確認問題 2

アイの答案です。「デキマシタ! 生まれ年を聞いて、そのまま表示するプログラムです」。この答案にバグはあるでしょうか。あるなら、その行はどこですか。

1#include <stdio.h>
2
3int main(void) {
4    int nen;
5    printf("生まれ年を入れてください: ");
6    scanf("%d", nen);
7    printf("%d 年生まれですね\n", nen);
8    return 0;
9}
答えを見る

判定:バグあり(6行目)

6行目の scanf に & がありません。正しくは scanf("%d", &nen); です。この誤りはコンパイルが通ってしまうことがあり、実行した瞬間に異常終了します(Segmentation fault)。scanf は「値」ではなく「書き込む場所」を必要とするからです。多くのコンパイラは format '%d' expects argument of type 'int *' と警告するので、警告を読む習慣がそのまま防具になります。

「はじめてのC」の目次

  1. 開発環境ゼロで始める——このポータルの使い方
  2. Hello, World! を解剖する
  3. main関数と#include
  4. コンパイルエラーと友達になる
  5. printfで画面に出す——書式指定の基本
  6. scanfで入力を受け取る
  7. scanfの罠——入力の恐怖を越える
  8. コメントとコードの整え方

もっと先へ:ポインタ・メモリ・ファイル入出力・セキュアコーディングを含む全26トラックと、段位検定・模試のフルセットは完全版に収録しています。

完全版の販売ページは準備中です。

ほかのトラック

📚 姉妹教材:手を動かして覚える Linux 教材 — Linuxとインフラの仕組みを地図で学ぶ

Web版