
C言語入門|scanfの使い方と注意点
ここまでで、文字列を
表示する・コピーする・連結する・生成する
といった処理を学んできましたね。
次はその逆、
キーボードから情報を受け取る
場面を扱います。
その代表が scanf 関数です。
ただし scanf は、
「便利だけど、油断すると一瞬で事故る」
という、かなりクセの強い関数でもあります。
この節では、
scanf が何をしている関数なのか
なぜ注意が必要なのか
を、メモリ視点でしっかり整理していきましょう。

scanfは「入力を書き込む」関数
scanf は、printf とよく似た見た目をしていますが、
中身はまったく逆の動きをします。
| 関数 | 役割 |
|---|---|
| printf | 値を読み取り、画面に出力する。 |
| scanf | 入力を読み取り、メモリに書き込む。 |
つまり scanf は、
値そのものではなく、書き込み先の場所
を教えてあげなければなりません。
ここが、初心者がつまずきやすい最大のポイントです。
scanfの書式
int scanf(const char* format, ...);| 引数 | 意味 |
|---|---|
| format | 入力用の書式文字列 |
| ... | 入力された値を書き込む先のアドレス |
| 戻り値 | 正常に読み取れた項目数(失敗時はEOF) |
scanf は、
書式文字列を見ながら、
キーボード入力を解析し、
指定されたメモリ領域へ書き込みます。
なぜ scanf では & が必要なのか
次のコードを見てみましょう。
サンプルプログラム
プロジェクト名:11-11-1 ソースファイル名: sample11-11-1.c
#include <stdio.h>
int main(void)
{
char player[64];
int score;
printf("プレイヤー名とスコアを入力してください。\n");
scanf("%s %d", player, &score);
printf("名前:%s\nスコア:%d\n", player, score);
return 0;
}ここで不思議に思うのが、
score だけ & が付いている点です。
printf と scanf の決定的な違い
この違いは、
後続引数に期待している型
が異なるために起こります。
| 書式 | printf が期待する型 | scanf が期待する型 |
|---|---|---|
| %d | int | int* |
| %f | double | float* |
| %s | char* | char* |
printf は
「表示したい値そのもの」
を受け取ります。
一方 scanf は
「ここに書き込んでください」
という アドレス を受け取ります。
そのため、
- int 型 → &変数名
- char 配列 → 配列名(先頭アドレス)
という指定が必要になるのです。
%s と 配列名が特別扱いな理由
scanf("%s", player);ここで &player を付けていないのは、
配列名が 特殊構文により先頭アドレスに変換される
からです。
| 書き方 | 実際に渡されているもの |
|---|---|
| player | &player[0] |
| &score | score のアドレス |
つまり、
%s でもアドレスはちゃんと渡されています。
scanfで最も危険なのは文字列入力
scanf の中で、
最も事故率が高いのが %s です。
理由は単純で、
入力文字数の上限を自動で制限しない
からです。
char name[16];
scanf("%s", name);この場合、
- 15文字以内なら安全
- 16文字以上入力されたら即オーバーラン
となります。
scanf は、
「入るだけ入れる」
という動作を平然と行います。
scanf と「3つの領域」の関係
scanf を安全に使うには、
文字列の「3つの領域」を常に意識する必要があります。
| 領域 | 意味 |
|---|---|
| 使用中 | 入力された文字列 + \0 |
| 使用可能 | 配列や malloc で確保した範囲 |
| 使用禁止 | 触れてはいけない領域 |
scanf は、
使用可能領域のサイズを一切見ません。
そのため、
使用可能領域を超えない保証を、呼び出し側がする必要があります。
scanfを使うときの心構え
scanf を使う場面では、
次のことを必ず意識しましょう。
| ポイント | 内容 |
|---|---|
| 書き込み先は必ずアドレス | 値ではなく場所を渡す。 |
| 配列サイズを把握する | %s は特に注意 |
| 戻り値を確認する | 入力失敗を見逃さない。 |
scanf は、
仕様を正しく理解して使えば便利ですが、
「なんとなく使う」と危険な関数です。
