
C言語基礎|scanf関数:書式付きの入力
scanfは便利だけど“クセも強い”!読み取りの仕組みと失敗パターンを押さえて、安全に入力を扱えるようになろう。
scanf は、キーボード入力を「型に合わせて変換しながら」読み取れる、とても強力な関数です。
でもその分、書式の書き方・空白の扱い・戻り値チェックをサボると、思わぬバグが起きやすいのも事実です。
この記事では、scanf の動き方を「指令」「変換指定」「失敗の種類」「戻り値」の観点で整理して、
“何が起きているか”をイメージできるように、表や図で丁寧に解説していきます。

scanf の役割(何をしてくれる関数?)
scanf は、第1引数の書式文字列(format)に書かれたルールに従って入力を読み取り、変換した値を第2引数以降の変数へ代入します。
書式(scanf 関数の形式)
int scanf(const char * restrict format, ...);
- 第1引数 format:入力をどう読むか(指令)を書く
- 第2引数以降:読み取った値を入れる場所(だいたいポインタ)
サンプルプログラム
「年齢(整数)と身長(小数)」を読み取って、結果を表示する例です。
ポイントは 戻り値のチェック と 入力失敗時の扱い です。
プロジェクト名:chap13-14-1 ソースファイル名:chap13-14-1.c
Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。
#include <stdio.h>
int main(void)
{
int age;
double height;
printf("年齢と身長を入力してください(例:20 170.5)\n");
printf("入力:");
/* scanfは成功した“項目数”を返すので、2なら両方読めたことになる */
if (scanf("%d%lf", &age, &height) == 2) {
printf("読み取り成功:年齢=%d、身長=%.1f\n", age, height);
} else {
printf("読み取り失敗:入力形式を確認してください。\n");
}
return 0;
}scanf の「指令」ってなに?
scanf の書式文字列に書ける“指令”は、大きく3種類です。
scanf の指令の種類
| 指令の種類 | 書式内の例 | 役割 | よくある注意点 |
|---|---|---|---|
| 空白類文字 | (スペースや改行など) | 入力側の空白類文字を読み飛ばす | 書式内に空白を入れると、入力側の改行なども飛ばす |
| 通常の文字 | , や : など | 入力に“その文字が来ること”を要求する | 一致しないとそこで失敗し、残りは読まれない |
| 変換指定 | %d, %lf など | 入力を数値や文字列として変換し代入 | 引数の型(ポインタ)を間違えると危険 |
表の説明
- scanf は、書式を左から順に“指令として実行”します。
- 途中で失敗したら、その時点で中断して戻ります(だから戻り値チェックが超大事)。
変換指定の構造
scanf の変換指定は、次の部品でできています。
図:scanf の変換指定の構造

図の説明
- 代入抑止 * を付けると「読み取るけど代入しない」になります。
- 最大フィールド幅は「読む最大文字数」を制限します(特に文字列で重要)。
- 長さ修飾子は「どの型に入れるか」を指定します。
- 変換指定子は「どう解釈するか(整数/小数/文字列など)」を決めます。
代入抑止文字 *(読み捨ての道具)
代入抑止 * の使いどころ
| 目的 | 書式例 | どう動く? | 例のイメージ |
|---|---|---|---|
| 途中の区切りを無視したい | %d%*c%d | 1個文字を読み取るが代入しない | 10,20 のカンマを捨てる |
| 不要な値を読み飛ばしたい | %*d%d | 最初の整数を捨てて次だけ読む | 先頭の番号を無視 |
表の説明
- 入力を“消費だけして捨てる”ことができます。
- ただし「何文字捨てるか」は変換指定子次第なので、設計は慎重に。
最大フィールド幅(入力を読みすぎないためのブレーキ)
scanf の怖いところの1つが「文字列の読み込みで、バッファを超える」事故です。
そこで最大フィールド幅が役立ちます。
最大フィールド幅の効果(特に s で重要)
| 例 | 意味 | メリット |
|---|---|---|
| %5s | 最大5文字まで読む(終端ナル文字は別) | 配列サイズに合わせて事故を防ぐ |
| %3d | 最大3文字の整数として読む | 想定外の長い数字を抑える |
表の説明
- 文字列入力(%s)は、幅指定がないと危険になりやすいです。
- 数値でも幅指定は使えますが、まずは戻り値チェックと組み合わせるのが王道です。
長さ修飾子(どの型に入れるかを決める)
scanf は「代入先の型」を正しく伝えないと危険です。
そのために長さ修飾子が用意されています。
よく使う長さ修飾子と組み合わせ(超実用寄り)
| 入れたい型 | 変換指定 | 代入先引数の型 |
|---|---|---|
| int | %d | int へのポインタ |
| long | %ld | long へのポインタ |
| long long | %lld | long long へのポインタ |
| double | %lf | double へのポインタ |
| long double | %Lf | long double へのポインタ |
| unsigned int | %u | unsigned int へのポインタ |
表の説明
- 特に覚えやすいのはこれです:
・double は %lf(printfと違うので注意!)
・long は l、long long は ll - 型と書式がズレると、読み取り結果が壊れたり未定義動作になったりします。
変換指定子(conversion specifier)の要点
質問文に出てきた代表格を、実務で使う観点でまとめます。
主要な変換指定子のまとめ
| 指定子 | 読み取るもの | 例入力 | 注意点 |
|---|---|---|---|
| d | 10進の符号付き整数 | -12 | 代入先は符号付き整数型 |
| i | 基数を自動判定する整数 | 012, 0x10 | 0で始まると8進、0xで16進になる |
| u | 10進の符号なし整数 | 42 | 負数を入れると期待通りにならない |
| x | 16進整数 | FF, 0x2a | 代入先は符号なしが基本 |
| f, e, g, a | 浮動小数点 | 3.14 | 代入先は float/double/long double(長さ修飾子で決まる) |
| s | 文字列(空白まで) | hello | 配列のサイズ管理が重要(幅指定推奨) |
| c | 文字(1文字) | A | 空白も読む(改行も読む)ので注意 |
| n | ここまでに読んだ文字数 | - | 代入のみ、入力は消費しない |
| % | %文字そのもの | % | %% と書く |
表の説明
- c は空白を読み飛ばさないので、直前の改行を拾ってしまう事故がよくあります。
- s は空白で止まるので「空白込みの行」を取りたいときは別の手段(fgetsなど)が向きます。
scanf の実行手順(本文の「手順」を図でつかむ)
scanf がどう進むかを、イメージ図にします。
図:scanf が入力を処理する流れ
書式文字列を左から読む
↓
指令を1つ実行する
↓
成功 → 次の指令へ
失敗 → そこで中断して戻る
↓
戻り値=代入できた項目数(0,1,2,...)
(1個も代入できない状態で入力が尽きた等の場合はEOFの可能性)
図の説明
- “全部成功するまで進む”のではなく、“途中で止まる”のが重要ポイントです。
- だから、読み取りが必要な個数ぶん成功したかを戻り値で判断します。
入力誤りと照合誤り(失敗の種類)
本文にある2種類の失敗は、原因と対処の考え方が違います。
失敗の種類
| 種類 | 何が起きた? | 例 | 対応の考え方 |
|---|---|---|---|
| 入力誤り | 入力が取れない/エラー | EOF、読み取りエラー | 入力終了として扱うことが多い |
| 照合誤り | 入力の形が期待と違う | 数字が欲しいのに abc | 入力を捨てる・再入力を促す |
表の説明
- “照合誤り”は「入力が残ったまま」になりがちです。
だからループで再入力させるなら、入力バッファの処理も考える必要があります。
scanf の戻り値(最重要ポイント)
scanf は 代入できた項目数 を返します。
戻り値の意味
| 戻り値 | 意味 |
|---|---|
| 期待した個数(例:2) | その個数ぶん代入できた(成功) |
| 0 | 1個も代入できない(照合誤りの可能性) |
| EOF | 入力が尽きた/読み取り不可能(入力誤り側) |
表の説明
- 例:scanf("%d%lf", &age, &height) なら、成功は戻り値 2。
- 戻り値チェックをしないと、変数が更新されていないのに処理が進むことがあります。
restrict ポインタ(この記事では“気にしすぎなくてOK”)
scanf の宣言に出てくる restrict は、コンパイラ最適化のためのヒントです。
学習段階では「標準ライブラリの宣言に付いていることがある」くらいで十分です。
今大事なのは、scanf の書式・引数・戻り値を正しく扱うことです。
