
scanf()関数はキーボードからの入力を受け取るC言語の標準関数だ。この関数はヘッダファイルに含まれ、別途ヘッダーファイルをインクルードする手間がない。
scanf()関数
scanf()関数の基本的な書式はとてもシンプルである。
scanf(書式指定文字列, &変数名);
ここで大切なのは、変数の前にアンパサンド(&)記号を付けることだ。これは変数のメモリ上のアドレスを指定するもので、入力された値をそのアドレスに格納する仕組みになっている。文字列配列を使うときはアンパサンドを付けない。
#include <stdio.h>
int main() {
int x;
printf("整数を入力してください: ");
scanf("%d", &x); // %dは整数の指定子
printf("あなたが入力した値は %d です。\n", x);
return 0;
}
出力
整数を入力してください: 3
出力
あなたが入力した値は 3 です。
scanf()を使う前には、必ずprintf()関数でユーザーに何を入力すべきか伝えよう。これがないとプログラムは入力待ちで止まり、ユーザーは何をすればよいか分からなくなる。
printf("あなたの年齢を入力してください: ");
scanf("%d", &age);
書式指定文字列には「%d」(整数)「%f」(小数)「%c」(一文字)などの変換指定子を使う。これらの指定子でどんな種類のデータを受け取るか指示できる。例えば整数なら「%d」を使い、入力された数値をint型変数に格納する。
よく使うフォーマット指定子
指定子 | 意味 | 対応する型 |
---|---|---|
%d | 整数 | int |
%f | 実数 | float |
%lf | 倍精度実数 | double |
%c | 文字 | char |
%s | 文字列 | char[] |
#include <stdio.h>
int main() {
int num;
printf("数字を入力してください: ");
scanf("%d", &num);
printf("入力された数字は %d です。\n", num);
return 0;
}
scanf()関数は複数の値を一度に受け取ることも可能だ。
int a, b;
scanf("%d %d", &a, &b); // 例:10 20 と入力すれば a=10, b=20
scanf("%d %f %c", &intVar, &floatVar, &charVar);
例
#include <stdio.h>
int main() {
int a, b;
float c;
printf("2つの整数と1つの小数を入力してください(例: 10 20 3.14):\n");
scanf("%d %d %f", &a, &b, &c);
printf("入力された値:\n");
printf("a = %d\n", a);
printf("b = %d\n", b);
printf("c = %.2f\n", c);
return 0;
}
出力
2つの整数と1つの小数を入力してください(例: 10 20 3.14):
22 7 1.2
入力された値:
a = 22
b = 7
c = 1.20
初心者がよく混乱するのが、scanf()の後にEnterキーを押したときの挙動である。入力バッファに残った改行文字が次のscanf()に影響することがある。この対策として文字を読み取る「%c」の前にスペースを入れると良い。
#include <stdio.h>
int main() {
int num;
char ch;
printf("整数を入力してください:\n");
scanf("%d", &num);
printf("文字を入力してください:\n");
scanf(" %c", &ch); // ← スペースを入れて改行文字をスキップ!
printf("入力された整数:%d\n", num);
printf("入力された文字:%c\n", ch);
return 0;
}
C言語で scanf() を使うときには変数の前に &(アドレス演算子)をつける。
scanf()は「変数の中身を変更する」関数である。
C言語では、関数に値を渡すとコピーされる。
だが、scanf() は、ユーザーの入力を「変数の中に入れる(=変更する)」必要があるので、「変数の場所(アドレス)」を教える必要がある。
たとえば次のようなコードがあったとする。
int x;
scanf("%d", &x);
ここで、&x を渡すことで、scanf() 関数の中では
*ptr = 入力された値;
のようなことをして、x の場所に実際に値を書き込む。
ptrが指しているメモリの場所に入力された値を代入するという意味だ。
変数にはそれぞれ「メモリ上の場所(アドレス)」がある。
&x は「xがどこにあるか(場所)」を意味する。
int x = 5;
printf("%p", &x); // たとえば 0x7ffeead0b8ac のような出力
& をつけないとどうなるのか
#include <stdio.h>
int main() {
int x;
scanf("%d", x); // ← & がない!!
printf("x = %d\n", x);
return 0;
}
「xという箱に数字を入れて!」
→ 正しくは:「箱の場所(住所)を教える」必要がある
→ &x を使えば「この箱に入れて」と言える
→ x だけ渡すと「この数字をメモリの住所として扱ってそこに書いて!」と誤解されて事故る
scanf() はポインタ(アドレス)を期待しているのに、単なる整数値を渡すと、その整数を「メモリの場所」として誤って解釈し、そこに書き込もうとする。その結果、未定義動作としてエラーになってしまうのだ。
scanf("%d", &x); OK。
scanf("%d", x); エラーになるか、未定義動作を引き起こす
scanfは &x を受け取って、その場所に入力値を書き込む関数であることは知っておこう。
scanf()関数の書式指定子と使い方
scanf()関数の真価は様々な書式指定子を使いこなすことで発揮される。ここでは主要な書式指定子とその使い方を解説する。
整数を読み取る書式指定子は用途に合わせて選べる。「%d」は標準的な整数(int型)を読み取る。より大きな範囲の整数には「%ld」(long int型)や「%lld」(long long int型)を使う。符号なし整数には「%u」を用いる。
int num;
long bigNum;
scanf("%d", &num); // 通常の整数
scanf("%ld", &bigNum); // 大きな整数
#include <stdio.h>
int main() {
int num;
long bigNum;
printf("通常の整数を入力してください: ");
scanf("%d", &num); // 通常の整数
printf("大きな整数を入力してください: ");
scanf("%ld", &bigNum); // long型の整数
printf("\n--- 入力された値 ---\n");
printf("num(int型) = %d\n", num);
printf("bigNum(long型)= %ld\n", bigNum);
return 0;
}
通常の整数を入力してください: 34
大きな整数を入力してください: 3345678
--- 入力された値 ---
num(int型) = 34
bigNum(long型)= 3345678
小数点を含む数値
小数点を含む数値は「%f」(float型)または「%lf」(double型)で読み取る。精度を指定するときは「%.2f」のようにドットと数字を間に入れる。
float price;
scanf("%f", &price); // 例: 12.34
#include <stdio.h>
int main() {
float price;
printf("価格を入力してください(例: 12.34):");
scanf("%f", &price); // 小数を読み取る
printf("入力された価格:%.2f\n", price); // 小数第2位まで表示
return 0;
}
価格を入力してください(例: 12.34):2.67
入力された価格:2.67
一文字だけ
一文字だけを読み取るなら「%c」を使う。文字の前にスペースを入れると、空白文字を無視できる。
char initial;
scanf(" %c", &initial); // スペースで始まる入力を防ぐ
#include <stdio.h>
int main() {
char initial;
printf("イニシャルを1文字入力してください:");
scanf(" %c", &initial); // 改行などを無視して1文字読み取る
printf("入力された文字:%c\n", initial);
return 0;
}
イニシャルを1文字入力してください:d
入力された文字:d
文字列
文字列を読み取るには「%s」を使う。この時、文字列は空白文字で区切られる。アンパサンド(&)は不要だ。
char name[50];
scanf("%s", name); // &は不要
例
#include <stdio.h>
int main() {
char name[50];
printf("名前を入力してください(例: Taro):");
scanf("%s", name); // &は不要、空白で区切られる
printf("入力された名前:%s\n", name);
return 0;
}
前を入力してください(例: Taro):jiro
入力された名前:jiro
char name[100] のような配列変数は、すでに「アドレス」だから & をつける必要がない。
&変数名 は「その変数のアドレス(メモリ上の場所)」を意味している。
char name[50];
scanf("%s", name); // ← & を付けない!
このとき name は配列である。
配列の変数名は、すでに先頭アドレスを意味している。
name は「&name[0](最初の文字のアドレス)」と同じ意味となり、scanf(“%s”, name); で アドレスを渡していることになる。つまり、&name や &name[0] をわざわざ書く必要がないのである。
空白を含む文字列全体
空白を含む文字列全体を読み取るには「%[^\n]」という特殊な書式を使う。これは改行文字以外のすべての文字を読み取る。
char fullName[100];
scanf(" %[^\n]", fullName); // 例: 山田 太郎
例
#include <stdio.h>
int main() {
char fullName[100];
printf("フルネームを入力してください(例: 山田 太郎):");
scanf(" %[^\n]", fullName); // 改行までのすべての文字を読み取る
printf("入力されたフルネーム:%s\n", fullName);
return 0;
}
フルネームを入力してください(例: 山田 太郎):鈴木 一郎
入力されたフルネーム:鈴木 一郎
- %[ … ]
「指定した文字の集合にマッチする限り文字を読み取る」という意味。 - ^(キャレット)
「否定」を意味する。ここでは「次の文字以外なら読み取る」という指示。 - \n
「改行文字」。
%[^\n] は、改行文字 \n が出てくるまでのすべての文字を読み取る」という意味になる。
幅の指定
幅の指定も可能だ。「%5d」は最大5桁の整数を、「%10s」は最大10文字の文字列を読み取る。
int code;
scanf("%5d", &code); // 最大5桁まで読み取る
#include <stdio.h>
int main() {
int code;
printf("最大5桁のコードを入力してください(例: 12345):");
scanf("%5d", &code); // 最大5桁まで読み取る
printf("入力されたコード:%d\n", code);
return 0;
}
最大5桁のコードを入力してください(例: 12345):3357
入力されたコード:3357
複数の値を一度に読み取る
複数の値を一度に読み取るときは、書式指定子をスペースで区切るか連続して書く。
int day, month, year;
scanf("%d/%d/%d", &day, &month, &year); // 例: 15/4/2023
#include <stdio.h>
int main() {
int day, month, year;
printf("日付を入力してください(形式: 15/4/2023):");
scanf("%d/%d/%d", &day, &month, &year); // 3つの整数を一度に読み取る
printf("入力された日付:%d年%d月%d日\n", year, month, day);
return 0;
}
付を入力してください(形式: 15/4/2023):13/3/2025
入力された日付:2025年3月13日
入力を検証する戻り値
入力を検証するには戻り値を確認する。scanf()は正常に読み取った項目の数を返す。
if (scanf("%d", &num) != 1) {
printf("正しい数値を入力してください\n");
}
#include <stdio.h>
int main() {
int num;
printf("数値を入力してください:");
if (scanf("%d", &num) != 1) {
printf("正しい数値を入力してください\n");
} else {
printf("入力された数値:%d\n", num);
}
return 0;
}
値を入力してください:667
入力された数値:667
```
数値を入力してください:fha
正しい数値を入力してください
scanf("%d", &num)
scanf() はユーザーからの入力を受け取る関数だ。”%d” は整数型の値を読み取るための書式指定子で、入力された値を num という変数に格納する。
&num は num のアドレスを渡すことを意味する。これでscanf() は入力された整数を num に格納できる。
scanf() は読み取った項目の数を戻り値として返す。例えば、scanf(“%d”, &num) で 1つの整数が正常に読み取られた場合、scanf() は 1 を返す。
もし、入力が整数ではなく文字列や空白などの場合、scanf() は 1 より小さい値(通常は 0)を返す。
if (scanf(“%d”, &num) != 1) の部分は、scanf() が 整数の読み取りに失敗した場合 の処理を行う。scanf() の戻り値が 1 でない(整数が読み取れなかった)場合に、if 文内のコードが実行される。
scanf()関数の書式指定子を理解すれば、多様なデータ形式を簡単に処理できる。ユーザーの入力に応じて動作するプログラムを作る第一歩である。
プログラム例で学ぶscanf()の実践的な使い方
実際のプログラムでscanf()関数をどう使うか具体例を見ていこう。初心者でも理解しやすいように、身近な状況を想定した例を挙げる。
ユーザー情報を取得するプログラム
まずはユーザー情報を収集する簡単なプログラムだ。名前、年齢、身長を入力し、それを表示する。
#include <stdio.h>
int main() {
char name[50];
int age;
float height;
printf("あなたの名前を入力してください: ");
scanf("%s", name);
printf("年齢を入力してください: ");
scanf("%d", &age);
printf("身長(cm)を入力してください: ");
scanf("%f", &height);
printf("\n--登録情報--\n");
printf("名前: %s\n", name);
printf("年齢: %d歳\n", age);
printf("身長: %.1fcm\n", height);
return 0;
}
このプログラムでは名前、年齢、身長を順番に入力する。注目すべき点は文字列を読み取る%s
ではアンパサンドを使わないことだ。また、名前に空白を含む場合は読み取れない制限がある。
空白を含む名前を入力する改良版
#include <stdio.h>
int main() {
char fullName[50];
int age;
printf("氏名(空白含む)を入力してください: ");
scanf(" %[^\n]", fullName);
printf("年齢を入力してください: ");
scanf("%d", &age);
printf("\n%sさん(%d歳)、こんにちは!\n", fullName, age);
return 0;
}
%[^\n]を使うことで、改行までのすべての文字(空白を含む)を読み取れる。これで「山田 太郎」のような空白を含む名前も正しく処理できる。
簡単な計算プログラム
次は、二つの数値を入力して四則演算を行うプログラムだ。
#include <stdio.h>
int main() {
float num1, num2;
printf("一つ目の数値を入力: ");
scanf("%f", &num1);
printf("二つ目の数値を入力: ");
scanf("%f", &num2);
printf("%.1f + %.1f = %.1f\n", num1, num2, num1 + num2);
printf("%.1f - %.1f = %.1f\n", num1, num2, num1 - num2);
printf("%.1f × %.1f = %.1f\n", num1, num2, num1 * num2);
if (num2 != 0) {
printf("%.1f ÷ %.1f = %.1f\n", num1, num2, num1 / num2);
} else {
printf("0での除算はできません\n");
}
return 0;
}
このプログラムでは、入力値を小数点以下1桁で表示するために%.1fを使っている。また、ゼロ除算を防ぐ条件分岐も入れた。
データ形式を指定した入力処理
次は、特定の形式でデータを入力するプログラムだ。例えば日付を「日/月/年」の形式で入力する。
#include <stdio.h>
int main() {
int day, month, year;
printf("日付を「日/月/年」の形式で入力してください(例: 15/4/2023): ");
if (scanf("%d/%d/%d", &day, &month, &year) == 3) {
printf("入力された日付: %d年%d月%d日\n", year, month, day);
} else {
printf("正しい形式で入力してください\n");
}
return 0;
}
このプログラムではscanf()の戻り値を使って、入力値の検証も行っている。正しく3つの値が読み取れたときのみ処理を続行する。
これらの例からわかるように、scanf()はユーザーから様々な形式のデータを受け取るのに便利な関数だ。
セキュリティリスク
scanf()関数を使う際の最大の危険性はバッファオーバーフローだ。
バッファオーバーフロー(buffer overflow)とは、用意されたメモリ領域(バッファ)を超えてデータを書き込んでしまうことを言う。
10文字分しか入らない箱(配列)に、20文字入れようとすると、あふれた10文字が隣のメモリ領域に侵入してしまう。プログラムのクラッシュ、や悪意のあるコードを実行される原因になりうる。
#include <stdio.h>
#include <string.h>
int main() {
char name[5];
// "abcdefghij" は 10文字 → name[5] に入りきらない
strcpy(name, "abcdefghij");
printf("名前:%s\n", name);
return 0;
}
ユーザーが配列のサイズを超える入力をすると、メモリ領域を破壊してしまう。これはプログラムのクラッシュやセキュリティ脆弱性の原因になる。
この対策として、入力サイズを制限する書式指定子を使う。
char name[10];
scanf("%9s", name); // 最大9文字(+ NULL終端)まで読み取る
より安全に行うには、fgets()関数がある。
fgets()関数
fgets() 関数は、文字列を安全に読み取るための標準Cライブラリ関数だ。scanf(“%s”, …) は空白で文字列を区切ってしまうが、fgets() は空白やタブ、改行を含む文字列もそのまま読み取れるため、入力をまるごと一行取得したいときに便利である。
char str[10];
fgets(str, sizeof(str), stdin);
このコードでは、最大「9文字」+「終端の \0」=10文字分しか読み取らない。
fgets() は必ず最大文字数 − 1 までしか読み取らないので、バッファ(配列)の中で必ず収まる。C言語では、文字列の終わりは \0(ヌル文字)で表すわけだが、fgets() は、読み取った文字の末尾に必ず \0 を付けてくれるのでこの点も安心して使える。
gets() や scanf(“%s”, …) のような関数は、入力の長さを制限できないか、または、しにくいため、長すぎる入力でバッファオーバーフローを起こす危険性がある。fgets() は、読み込む長さを自分で指定するので「メモリの外にはみ出す」危険がないわけだ。
fgets( 文字列を格納する配列, 最大文字数, 入力元 );
char name[50];
fgets(name, sizeof(name), stdin);
- name 読み取った文字列を入れる配列
- sizeof(name) 読み取る最大の文字数(この例では50)(終端の \0 を含む)
- stdin 入力元(ここではキーボードからの標準入力)
たとえば、キーボードから「Yamada Taro」と入力し、Enterキーを押すと
- fgets() は「最大49文字」+「終端の \0」=50バイト以内で読み取る。
- 入力された文字列が name[] に保存される。
- さらに「改行文字 \n も一緒に入る」ことに注意
fgets(name, sizeof(name), stdin) のときも name がアドレスそのものだからscanf()関数同様に & は不要である。
例
#include <stdio.h>
int main() {
char name[100];
printf("名前を入力してください:");
fgets(name, sizeof(name), stdin);
printf("入力された名前:%s", name);
return 0;
}
名前を入力してください:田中 太郎
入力された名前:田中 太郎
fgets() と scanf(“%s”, …) の動作の違いはどんな点にあるのだろうか。
fgets()とscanf(“%s”, …)の違い
項目 | fgets() | scanf(“%s”, …) |
---|---|---|
空白を含む入力 | 読める(OK) | 読めない(空白で区切られる) |
改行文字(\n) | 残る(そのまま入る) | 入らない(自動で切られる) |
最大文字数の制御 | 自分で指定できて安全 | 自分で制御しないと危険(バッファオーバーフローの可能性) |
推奨される場面 | 1行まるごと読み取りたいとき | 単語1つ(空白なし)を読みたいとき |
それぞれの例で比較
- fgets() の例
char name[100];
fgets(name, sizeof(name), stdin);
- 例:Yamada Taro と入力すると → name に “Yamada Taro\n” が入る
- 空白も含めて、1行まるごと読み込む
- scanf(“%s”, name) の例
char name[100];
scanf("%s", name);
- 例:「Yamada Taro」 と入力すると → 「name」 に 「Yamada」 だけが入る
- 空白文字(スペース、タブなど)で入力が切れてしまう
- セキュリティと安全性の観点
関数 | 安全性 | 理由 |
---|---|---|
fgets() | 安全 | 最大文字数を明示的に指定できる(バッファオーバーフロー対策) |
scanf(“%s”) | 危険 | 最大文字数を指定しないと、想定より長い入力でメモリ破壊の可能性あり |
scanf(“%99s”, name); のようにサイズ指定すれば少しマシになるが、それでもfgets()のほうが柔軟で安全だ。
