C言語で実用的なプログラムを書くためには、キーボードから値を入力して処理することが欠かせません。
その中心的な役割を担う関数がscanfです。
本記事では、C言語初心者の方に向けて、scanfの基本構文から、よくあるエラー、入力バッファの問題、安全な書き方までを丁寧に解説します。
単に暗記するのではなく、背景の仕組みを理解しながら読み進めてください。
scanfとは何か

scanfの役割と特徴
scanfは、C言語の標準ライブラリstdio.hに含まれる標準入力から値を読み取るための関数です。
一般的には、キーボードからの入力を受け取るために使用します。
scanfの主な特徴を文章で整理すると、次のようになります。
まず、プログラム中でscanfを呼び出すと、ユーザーがキーボードで入力してEnterキーを押すまで処理が一時停止します。
つまり、ユーザーの入力待ちをするための「入口」として働きます。
次に、scanfは"%d"や"%f"などのフォーマット指定子に従って、入力された文字列を整数や小数、文字などに変換して、指定した変数に格納します。
また、scanfは入力に失敗した場合や、想定と違う形式のデータが入力された場合に、それを戻り値で知らせる機能を持っています。
これを利用することで、安全な入力処理が可能になります。
このように、scanfは単に値を受け取るだけでなく、「どういう形式の値を、どの変数に入れるか」を指定しながら入力を処理する関数だと理解すると良いです。
標準入力(stdin)とキーボード入力の関係
C言語では、キーボード入力は内部的には標準入力(stdin)という仕組みを通じて取り扱われます。
通常、コンソールアプリケーションを実行したとき、標準入力は自動的にキーボードに接続されています。
そのため、scanfを使うとキーボードから入力を受け取っているように見えますが、正確には標準入力ストリーム(stdin)からデータを読み取っているのです。
表にして整理すると次のようになります。
| 名称 | 役割 | 典型的な入出力先 |
|---|---|---|
| stdin | 標準入力 | キーボード |
| stdout | 標準出力 | 画面(コンソール) |
| stderr | 標準エラー出力 | 画面(コンソール) |
初心者の段階では、「scanfはstdinから読み取り、通常stdinはキーボードにつながっている」と理解しておけば十分です。
この考え方は、ファイル入力やリダイレクトなど、もう少し進んだ内容を学ぶときに役立ちます。
printfとの違いとセットで覚えるポイント
printfとscanfは、対になる存在として扱われます。
- printfは標準出力(stdout)に値を出力する関数です。
- scanfは標準入力(stdin)から値を読み取る関数です。
どちらもstdio.hに含まれており、フォーマット指定子を使って扱うデータ型を指定する点が共通しています。
セットで覚えたいポイントとして、次のような対応関係があります。
| データ型の意味 | printfの例 | scanfの例 |
|---|---|---|
| 整数(int) | printf("%d", x); | scanf("%d", &x); |
| 実数(float) | printf("%f", f); | scanf("%f", &f); |
| 実数(double) | printf("%lf", d); | scanf("%lf", &d); |
| 文字(char) | printf("%c", c); | scanf("%c", &c); |
特に初心者が注意すべきなのは、scanfでは変数の前に&(アンパサンド)を付ける必要があるという点です。
printfでは&を付けないため、混同しやすくなります。
この違いは後ほど詳しく解説します。
scanfの基本的な使い方

scanfの基本構文とフォーマット指定子
scanfの基本的な書き方は、次のようになります。
#include <stdio.h>
int main(void) {
int x;
// 整数を1つ読み取る基本的なscanfの例
printf("整数を入力してください: ");
scanf("%d", &x); // "%d"に従って整数として読み取り、xに代入
printf("入力された値は%dです。\n", x);
return 0;
}
基本構文は次のように表せます。
scanf("フォーマット文字列", &変数1, &変数2, ...);
ここで重要なのは、フォーマット文字列の中に書くフォーマット指定子です。
これは、入力をどの型として解釈するかを指定します。
代表的なフォーマット指定子を表にまとめます。
| フォーマット指定子 | 解釈する型 | 対応する変数の型(代表例) |
|---|---|---|
%d | 10進整数 | int |
%u | 符号なし10進整数 | unsigned int |
%f | 実数(float用) | float |
%lf | 実数(double用) | double |
%c | 1文字 | char |
%s | 文字列(空白まで) | char配列 |
フォーマット指定子と変数の型が一致していないと、バグやクラッシュの原因になります。
この点は、後ほど詳しく触れます。
整数入力(%d)の書き方と例
整数の入力は、scanfの中でも最も基本的な使い方です。
整数を1つ読み取る場合は%dを使い、変数にはint型を用います。
#include <stdio.h>
int main(void) {
int age; // 年齢を保存するための変数
printf("あなたの年齢を入力してください: ");
// %dで整数として読み取り、ageのアドレス(&age)に格納します
if (scanf("%d", &age) != 1) { // 戻り値で入力成功を確認
printf("整数として認識できませんでした。\n");
return 1;
}
printf("あなたは%d歳ですね。\n", age);
return 0;
}
出力例(ユーザーが25と入力した場合):
あなたの年齢を入力してください: 25
あなたは25歳ですね。
ここでのポイントは、ユーザーは数字を文字として入力しているが、scanfはそれを整数に変換してから変数に格納しているという点です。
この変換を自動で行ってくれるところがscanfの便利なところです。
小数入力(%f %lf)の書き方と例
実数(小数)を入力する場合は、%fまたは%lfを使います。
ただし、floatとdoubleで使うフォーマット指定子が異なることに注意が必要です。
float型 →%fdouble型 →%lf
#include <stdio.h>
int main(void) {
float temperature; // float型で気温を保存
double height; // double型で身長を保存
printf("現在の気温を入力してください(例: 23.5): ");
scanf("%f", &temperature); // floatなので%fを使用
printf("あなたの身長を入力してください(例: 170.2): ");
scanf("%lf", &height); // doubleなので%lfを使用
printf("気温は%.1f度、身長は%.1lfcmですね。\n", temperature, height);
return 0;
}
現在の気温を入力してください(例: 23.5): 22.8
あなたの身長を入力してください(例: 170.2): 171.5
気温は22.8度、身長は171.5cmですね。
double型に対して%cst-code>%fを使ってしまう初心者のミスはとても多いため、「scanfでdoubleは%lf」とセットで覚えておきましょう。
文字入力(%c)と文字列入力(%s)の違い
文字と文字列は、初心者にとって混同しやすい概念です。
- 文字(char)は、1文字だけを扱う型です。フォーマット指定子は
%cです。 - 文字列(char配列)は、複数文字の並びを扱う型です。フォーマット指定子は
%sです。
単一文字の入力例を見てみます。
#include <stdio.h>
int main(void) {
char grade; // 成績(例: A, B, C)を1文字で保存
printf("成績を1文字で入力してください(A〜F): ");
scanf("%c", &grade); // 1文字だけ読み取る
printf("入力された成績は%cです。\n", grade);
return 0;
}
文字列の入力例は次の通りです。
#include <stdio.h>
int main(void) {
char name[20]; // 20文字までの名前を保存する配列
printf("あなたの名前を入力してください(スペースなし): ");
scanf("%19s", name); // 最大19文字まで読み取り(終端の#include <stdio.h>
int main(void) {
char name[20]; // 20文字までの名前を保存する配列
printf("あなたの名前を入力してください(スペースなし): ");
scanf("%19s", name); // 最大19文字まで読み取り(終端の\0の分を残す)
printf("こんにちは、%sさん。\n", name);
return 0;
}
の分を残す)
printf("こんにちは、%sさん。\n", name);
return 0;
}
出力例(ユーザーがTaroと入力した場合):
あなたの名前を入力してください(スペースなし): Taro
こんにちは、Taroさん。
ここで重要なのは、文字列用のscanfでは&を付けないことです。
配列名(ここではname)はそれ自体が先頭要素のアドレスを表すため、&nameとは書きません。
これは初心者にとって戸惑いやすいポイントなので、意識して区別するようにしましょう。
また、この例では、入力できる最大文字数を%19sのように指定している点も安全上重要です。
これについては後の「安全な書き方」で再度説明します。
&アンパサンドの意味と付け忘れ防止ポイント
scanfを書くときに頻発するエラーが、&(アンパサンド)の付け忘れです。
ここで、そもそも&が何を意味しているかを整理しておきます。
&は「変数のアドレス(場所)を取る演算子」です。
scanfは、「どのメモリの場所に入力した値を保存するか」を知る必要があるため、変数そのものではなく、その変数がメモリ上のどこにあるかというアドレスを受け取ります。
そのため、整数や小数、文字を読み取るときは、次のように書かなければなりません。
int x;ならscanf("%d", &x);float f;ならscanf("%f", &f);char c;ならscanf("%c", &c);
&を付け忘れると、scanfは「その時点のxの値」をアドレスだと勘違いしてしまい、全く別の場所に書き込もうとしてプログラムが異常終了する危険があります。
付け忘れを防ぐためのコツとして、「scanfは必ずアドレスを渡す関数」と意識し、変数宣言とセットで書く練習がおすすめです。
#include <stdio.h>
int main(void) {
int x;
printf("整数を入力してください: ");
// &を付け忘れると危険
// scanf("%d", x); // ← これは誤り
scanf("%d", &x); // 正しい書き方
printf("入力された整数は%dです。\n", x);
return 0;
}
文字列用の%sでは&を付けないことも合わせて覚えると、混乱が減ります。
scanfの注意点とよくあるミス
改行やスペースが残るバッファ問題とは
scanfを使うときに多くの初心者がつまずくのが、入力バッファに残る改行やスペースの問題です。
ユーザーがキーボードから入力してEnterキーを押すと、標準入力には次のような文字の列が入ります。
1 2 3(Enter)と入力した場合、実際には'1' ' ' '2' ' ' '3' '\n'という文字が並びます。
scanfはこの入力バッファから必要な分だけ読み取り、使わなかった文字はそのまま残します。
ここで問題になるのが、改行文字(‘\n’)です。
例えば、整数を読んだあとに文字を読もうとすると、整数入力のときに押したEnterの改行が次の入力として扱われてしまうことがあります。
この現象が「バッファに改行が残ってしまう」という問題の正体です。
連続して入力するときの改行対策
整数や小数のあとに%cなどを使って文字を読み取ると、意図しない挙動をすることがあります。
次の例を見てください。
#include <stdio.h>
int main(void) {
int number;
char ch;
printf("整数を入力してください: ");
scanf("%d", &number);
printf("次に、1文字を入力してください: ");
scanf("%c", &ch); // ここで問題が起きやすい
printf("number = %d, ch = '%c'\n", number, ch);
return 0;
}
実際には次のような動きになります。
- ユーザーが
10(Enter)と入力すると、"10\n"がバッファに入ります。 scanf("%d", &number);は10だけを読み取り、'\n'はバッファに残ります。- 次の
scanf("%c", &ch);は、残っていた'\n'をそのまま1文字として読み取ります。
その結果、ユーザーが新しい文字を入力する前に、chには改行文字が入ってしまいます。
この問題を避ける一般的な方法として、入力前に空白を読み飛ばすテクニックがあります。
%cや%sの前にスペースを1つ書くことで、スペースや改行をスキップできます。
#include <stdio.h>
int main(void) {
int number;
char ch;
printf("整数を入力してください: ");
scanf("%d", &number);
printf("次に、1文字を入力してください: ");
scanf(" %c", &ch); // 先頭にスペースを入れて空白文字を読み飛ばす
printf("number = %d, ch = '%c'\n", number, ch);
return 0;
}
整数を入力してください: 10
次に、1文字を入力してください: A
number = 10, ch = 'A'
フォーマット文字列の先頭のスペースは、「空白文字(スペース・改行・タブなど)を無視する」という特別な意味を持ちます。
連続入力のときは、この使い方を覚えておくと便利です。
スペースを含む文字列を読み取れない理由
%sによる文字列入力には、もう1つ重要な制限があります。
%sはスペース、タブ、改行で入力を区切るというルールがあるため、スペースを含む文字列を1度に読み取ることができません。
例えば、次のプログラムで「Taro Yamada」と入力してみます。
#include <stdio.h>
int main(void) {
char name[50];
printf("あなたのフルネームを入力してください: ");
scanf("%49s", name); // スペースまでしか読み取られない
printf("入力された名前: %s\n", name);
return 0;
}
出力例(ユーザーがTaro Yamadaと入力した場合):
あなたのフルネームを入力してください: Taro Yamada
入力された名前: Taro
このように、スペースより後ろの部分はバッファに残ってしまい、別の入力として扱われることになります。
スペースを含む文字列を読み取りたい場合は、scanfではなくfgetsを使うのが一般的です。
この点は後ほどのテクニックの章で改めて紹介します。
%cで意図しない文字を読んでしまう原因
%cによる1文字読み取りでは、先ほどの改行問題に加えて、入力バッファに残っているどんな文字でもそのまま読んでしまうという特徴があります。
%dや%fなどは、先頭の空白文字(スペースや改行など)を自動的にスキップしますが、%cは一切スキップせず、そのまま1文字だけ読み取る仕様になっています。
そのため、意図せず改行やスペースがchar変数に入ってしまうことが多くなります。
対策としては、先ほど紹介したように" %c"のようにフォーマット指定子の前にスペースを入れ、空白文字を読み飛ばす書き方を意識すると良いです。
scanf(" %c", &ch); // 空白を読み飛ばしてから1文字読む
この1文字分のスペースがあるかどうかで挙動が大きく変わるため、%cのときは” %c”と書く習慣を付けるのがおすすめです。
フォーマット指定子と変数型の不一致エラー
フォーマット指定子と変数の型が一致していない場合、コンパイルは通っても実行時におかしな挙動が発生します。
この種のバグは非常に見つけにくいため、意識的に避ける必要があります。
よくある間違いとして、次のようなものがあります。
#include <stdio.h>
int main(void) {
int x;
double d;
// 誤った例1: int変数xに%lfで読み込もうとしている
// scanf("%lf", &x); // 型不一致(危険)
// 誤った例2: double変数dに%dで読み込もうとしている
// scanf("%d", &d); // 型不一致(危険)
return 0;
}
このような場合、scanfは「その型に合わせてメモリを書き換える」ため、全く意図しない場所のメモリが壊れてしまうことがあります。
対策としては、次のように「変数宣言→フォーマット指定子→printfとの対応」をセットで確認する習慣を持つことが重要です。
| 変数の型 | scanfで使う指定子 | printfで使う指定子(代表) |
|---|---|---|
| int | %d | %d |
| unsigned int | %u | %u |
| float | %f | %f |
| double | %lf | %lf or %f(処理系依存に注意) |
| char | %c | %c |
| char配列 | %s | %s |
「型の表」と見比べながら書くのは、初心者にとって非常に有効な練習になります。
scanfの戻り値で入力の成功・失敗を確認する方法
scanfは戻り値として「正常に入力できた項目の数」を返します。
これを利用することで、入力が正しく行われたかどうかをプログラム側で確認できます。
例えば、整数を1つ読み取りたい場合は、次のように書きます。
#include <stdio.h>
int main(void) {
int x;
printf("整数を入力してください: ");
int ret = scanf("%d", &x); // 戻り値を受け取る
if (ret != 1) {
// 1個の整数を読み取れなかった場合(入力エラー)
printf("整数として認識できませんでした。\n");
return 1;
}
printf("入力された整数は%dです。\n", x);
return 0;
}
出力例1(ユーザーが100と入力した場合):
整数を入力してください: 100
入力された整数は100です。
出力例2(ユーザーがabcと入力した場合):
整数を入力してください: abc
整数として認識できませんでした。
このように、戻り値をチェックせずにそのまま変数を使うと、未定義の値を扱ってしまう危険があります。
初心者のうちから、入力値を信じるのではなく、必ず確認するという姿勢を身に付けておくと良いです。
scanfの実践テクニックと安全な書き方
複数の値を一度に入力する書き方
scanfは、一度の呼び出しで複数の値を読み取ることができます。
例えば、「整数2つをスペース区切りで入力させる」場合は次のように書けます。
#include <stdio.h>
int main(void) {
int a, b;
printf("2つの整数をスペース区切りで入力してください: ");
// "%d%d"でもよいですが、見やすさのためにスペースを入れています
if (scanf("%d %d", &a, &b) != 2) { // 2つ読み取れなければエラー
printf("2つの整数を正しく入力してください。\n");
return 1;
}
printf("a = %d, b = %d\n", a, b);
return 0;
}
出力例(ユーザーが10 20と入力した場合):
2つの整数をスペース区切りで入力してください: 10 20
a = 10, b = 20
ここでのポイントは、フォーマット文字列中の空白は「任意個数の空白文字にマッチする」という性質です。
ユーザーがスペース1つでもタブでも改行でも、まとめて扱ってくれます。
また、戻り値を2と比較して、2つの整数が正しく読み取れたかどうかをチェックしている点も重要です。
複数入力を扱うときほど、戻り値チェックの重要性が増します。
スペースを含む文字列を読む方法
すでに述べたように、scanf("%s", ...)ではスペースを含む文字列を読み取ることはできません。
そこでよく使われるのが、行単位で文字列を読み取るfgets関数です。
fgetsは、改行または指定した最大文字数に達するまで、1行分の文字列を読み取ります。
#include <stdio.h>
int main(void) {
char line[100];
printf("メッセージを入力してください(スペースを含んでもOK): ");
// 標準入力(stdin)から最大99文字を読み取る(最後の1文字は終端用)
if (fgets(line, sizeof(line), stdin) == NULL) {
printf("入力に失敗しました。\n");
return 1;
}
printf("入力されたメッセージ: %s", line);
return 0;
}
出力例(ユーザーがHello World!と入力した場合):
メッセージを入力してください(スペースを含んでもOK): Hello World!
入力されたメッセージ: Hello World!
このように、スペースを含む文字列を扱う場合は、scanfではなくfgetsを使う方が安全で直感的です。
初心者のうちは「数値はscanf、1行の文字列はfgets」と覚えておくと整理しやすくなります。
入力エラー時の再入力ループの例
実際のプログラムでは、ユーザーが必ず正しい形式で入力してくれるとは限りません。
そのため、入力エラー時に「もう一度入力してください」と促す仕組みを作ると、使いやすいプログラムになります。
整数入力の再入力ループの例を示します。
#include <stdio.h>
int main(void) {
int x;
int ret;
while (1) {
printf("1〜100の整数を入力してください: ");
ret = scanf("%d", &x);
if (ret != 1) {
// 整数以外が入力された場合
printf("整数ではありません。もう一度入力してください。\n");
// 誤った入力を捨てるため、残りの文字列を読み飛ばす
int ch;
while ((ch = getchar()) != '\n' && ch != EOF) {
; // 何もしない(捨てるだけ)
}
continue; // 先頭に戻る
}
// 範囲チェック
if (x < 1 || x > 100) {
printf("1〜100の範囲で入力してください。\n");
continue;
}
// 正常に読み取れていて、範囲もOKならループを抜ける
break;
}
printf("正しい入力がされました: %d\n", x);
return 0;
}
出力例(いくつか失敗した後に成功した場合):
1〜100の整数を入力してください: abc
整数ではありません。もう一度入力してください。
1〜100の整数を入力してください: 150
1〜100の範囲で入力してください。
1〜100の整数を入力してください: 42
正しい入力がされました: 42
ここで使っているgetchar()とループは、誤った入力をバッファから捨てる(クリアする)ための典型的なテクニックです。
次の節で、バッファクリアについてもう少し掘り下げて説明します。
バッファクリアの考え方と実装パターン
「バッファクリア」とは、入力バッファ(stdin)に残っている不要な文字列を読み捨てることを指します。
これを行わないと、次の入力時に残りの文字列が誤って使用されてしまうことがあります。
先ほどの再入力ループの例で使ったパターンを、関数としてまとめておくと便利です。
#include <stdio.h>
// 入力バッファに残っている文字を改行まで捨てる関数
void clear_stdin_buffer(void) {
int ch;
// '\n'(改行)かEOF(入力終端)に達するまで読み捨てる
while ((ch = getchar()) != '\n' && ch != EOF) {
; // 何もしない(読み捨て)
}
}
int main(void) {
int x;
int ret;
while (1) {
printf("整数を入力してください: ");
ret = scanf("%d", &x);
if (ret != 1) {
printf("整数ではありません。再入力してください。\n");
clear_stdin_buffer(); // 関数を使ってバッファをクリア
continue;
}
clear_stdin_buffer(); // 正常入力後に、行末の改行を捨てる場合にも使える
printf("入力された整数は%dです。\n", x);
break;
}
return 0;
}
整数を入力してください: xyz
整数ではありません。再入力してください。
整数を入力してください: 123
入力された整数は123です。
このバッファクリアの関数は、実験的なプログラムでも実務的なコードでもよく使われるパターンです。
特に、scanfとfgetsを組み合わせる場合などに重宝します。
初心者向けscanfのチェックリストと書き方まとめ
最後に、初心者がscanfを安全に使うためのチェックポイントを整理します。
文章で順に確認してみてください。
まず、変数の宣言とフォーマット指定子の対応を確認します。
intなら%d、floatなら%f、doubleなら%lf、charなら%c、文字列なら%sという対応関係が崩れていないかを見直します。
次に、アンパサンド(&)の有無を確認します。
intやfloat、double、charには&が必要ですが、char配列(文字列)では不要です。
&の付け忘れは実行時エラーの原因になるため、特に注意します。
その後、戻り値をチェックしているかを確認します。
読み取る項目数とscanfの戻り値が一致しているかを条件分岐に利用することで、入力エラーに対応しやすくなります。
さらに、バッファ問題に注意します。
特に、整数や小数を読んだ後に%cや%sで入力を続けるときには、" %c"のように先頭にスペースを入れる、あるいはバッファクリアを挟むといった対策を行います。
また、文字列の最大長の指定も忘れないようにします。
%19sのように最大文字数を指定することで、バッファオーバーフローの危険性を減らせます。
最後に、「スペースを含む文字列を読みたいときはfgets」というルールを思い出し、用途に応じてscanfとfgetsを使い分けることが大切です。
まとめ
本記事では、C言語の標準入力関数であるscanfについて、基本構文からフォーマット指定子、&の意味、典型的なバグの原因であるバッファ問題や型不一致、そして実践的なテクニックまでを解説しました。
scanfは便利ですが、その仕様を理解せずに使うと予想外の挙動を引き起こします。
戻り値の確認やバッファクリア、文字列の最大長指定などのポイントを押さえ、安全な入力を心掛けてください。
慣れてきたら、用途に応じてfgetsや他の入力手法との使い分けも学ぶと、より堅牢なCプログラムが書けるようになります。
