C言語のきほん|文字列を入力する

文字列が入力できると、プログラムはぐっと実用的になる。scanfで安全に文字列を受け取ろう。

整数や小数、1文字の入力ができるようになると、次に必要になるのが 文字列の入力 です。
名前、コマンド、検索キーワード、ファイル名など、実際のプログラムでは文字列を入力する機会がとても多いですよね。

C言語では、scanf を使って文字列を入力し、char 型の配列に格納できます。
ただし、文字列入力には注意点があります。

  • 文字列は char の配列で扱う
  • 入力が長すぎると危険(バッファオーバーフロー)
  • %s は空白で区切られる(空白を含む文は読めない)

この節では、まず scanf で文字列を入力する基本をしっかり押さえます。
文字列の詳しい扱い(配列や \0 など)は後の節でより深く学ぶとして、ここでは「安全に入力して表示できる」ことを目標に進めます。

文字列はchar型配列に入れる

C言語では、文字列は char の並びとして扱います。
そのため、文字列を受け取るには char 型の配列を用意します。

例として、次のように書きます。

char name[50];

これは「50文字分の箱」を用意しているイメージです。

\0 の分を1文字確保する理由

C言語の文字列は、末尾に \0(ナル文字)が付いて終わります。
入力された文字列が 49文字 でも、最後に \0 を入れる必要があるので、配列サイズ 50 の場合は 最大49文字まで が安全圏になります。

だから、100要素の配列なら最大99文字、という考え方になります。

scanfで文字列を入力する基本

文字列入力で使う変換指定は %s です。

基本形は次のようになります。

scanf("%s", str);

ここで大事なのは、配列名には & を付けない ことです。

配列名に&を付けない理由

scanf で整数を読むときは &n のように & を付けました。
でも文字列入力では付けません。

  • int などの変数:scanf に変数のアドレスを渡すため & が必要
  • char配列:配列名そのものが先頭要素のアドレスを表す

つまり、文字列入力では配列名をそのまま渡せば、先頭の場所がわかるということです。

int n;        → &n が必要
char str[50]; → str だけで先頭アドレスを渡せる

フィールド幅を指定して安全に入力する

scanf の %s は、何も指定しないと長い入力が入ってきたときに配列の外まで書き込んでしまう危険があります。
これがバッファオーバーフローで、メモリ破壊につながることがあるので要注意です。

そこで、%s には 入力フィールド幅 を付けるのが基本です。

たとえば、配列が 50 要素なら、最大入力を 49 文字に制限します。

scanf("%49s", name);

この 49 は、終端の \0 の分を除いた最大入力長です。

安全指定の覚え方

配列サイズが N なら
フィールド幅は N-1

%s は空白で入力が止まる

%s は便利ですが、空白(スペース、タブ、改行)を読むと、その時点で入力を終了します。

たとえば、次の入力をした場合、

Hello World!

%s で読むと入るのは Hello までです。
World! は次の入力として残ります。

空白を含む文章を読みたい場合は、別の方法が必要です(代表例が fgets)。
この節ではまず %s の性質を理解しておけばOKです。

サンプルプログラムで確認しよう

ファイル名:5_9_1.c

#include <stdio.h>

int main(void)
{
    char keyword[40];   /* 文字列を入力する配列(最大39文字 + 終端) */

    printf("検索キーワードを入力してください > ");
    scanf("%39s", keyword);  /* 入力長を制限して安全に読み取る */

    printf("入力されたキーワード: %s\n", keyword);

    return 0;
}

実行結果例(入力はユーザーが打ったもの)

検索キーワードを入力してください > linux
入力されたキーワード: linux

このサンプルのポイント解説

配列サイズと入力幅がセット

char keyword[40];
scanf("%39s", keyword);

この組み合わせが安全入力の基本です。

  • keyword は 40文字分の箱
  • 入力は最大39文字まで
  • 残り1文字は \0 のため

& を付けない

scanf("%39s", keyword);

ここでは &keyword にしません。
配列名 keyword だけで先頭アドレスを渡せるからです。

空白が入ると途中で止まる

たとえば入力が

hello world

だと、keyword に入るのは hello までです。
これは %s の仕様なので、まずは「空白は区切りになる」と覚えておきましょう。

scanfの主な変換指定をまとめて確認

ここで、よく使う scanf の変換指定を整理しておくと、入力プログラムが作りやすくなります。

変換指定説明対応する型の例入力例
%d10進整数int123
%u符号なし整数unsigned int123
%o8進整数unsigned int17、075
%x16進整数unsigned int7F、a9
%f実数(float)float3.14
%lf実数(double)double3.14159
%c1文字charA
%s文字列char配列Hello

特に覚えておくと役立つセットはこれです。

  • int → %d
  • double → %lf
  • char → %c
  • 文字列(配列) → %s

実践問題

次のプログラムは、ユーザーに「ニックネーム」「好きな記号(1文字)」「経験年数(整数)」「目標貯金額(実数)」を入力してもらい、最後にまとめて表示します。
空欄を埋めて、実行結果例のように動くようにしてください。

#include <stdio.h>

int main(void)
{
    char symbol;
    int years;
    double savings;
    char nickname[30];

    /* データの入力 */
    printf("ニックネームを入力してください > ");
    ➀

    printf("好きな記号を1文字で入力してください > ");
    ➁

    printf("経験年数を入力してください > ");
    ➂

    printf("目標貯金額を入力してください(単位:円)> ");
    ➃

    /* 入力されたデータを出力 */
    printf("\n入力された内容:\n");
    printf("ニックネーム: %s\n", nickname);
    printf("好きな記号  : %c\n", symbol);
    printf("経験年数    : %d\n", years);
    printf("目標貯金額  : %.1f 円\n", savings);

    return 0;
}

実行結果例(入力はユーザーが打ったもの)

ニックネームを入力してください > suzuki
好きな記号を1文字で入力してください > #
経験年数を入力してください > 3
目標貯金額を入力してください(単位:円)> 50000.5

入力された内容:
ニックネーム: suzuki
好きな記号  : #
経験年数    : 3
目標貯金額  : 50000.5 円

解答例

#include <stdio.h>

int main(void)
{
    char symbol;
    int years;
    double savings;
    char nickname[30];

    /* データの入力 */
    printf("ニックネームを入力してください > ");
    scanf("%29s", nickname);

    printf("好きな記号を1文字で入力してください > ");
    scanf(" %c", &symbol);

    printf("経験年数を入力してください > ");
    scanf("%d", &years);

    printf("目標貯金額を入力してください(単位:円)> ");
    scanf("%lf", &savings);

    /* 入力されたデータを出力 */
    printf("\n入力された内容:\n");
    printf("ニックネーム: %s\n", nickname);
    printf("好きな記号  : %c\n", symbol);
    printf("経験年数    : %d\n", years);
    printf("目標貯金額  : %.1f 円\n", savings);

    return 0;
}

解説

① 文字列入力は%s、&は付けない、幅を付ける

scanf("%29s", nickname);
  • nickname は char配列なので & は付けません
  • 配列サイズが30なので、最大入力は29文字に制限します(\0 の分を残すため)

② 1文字入力は% c が安全

scanf(" %c", &symbol);

%c の前の半角スペースがポイントです。
これがあると、直前の入力で残った改行などを飛ばして、次の1文字を正しく読めます。

③ int入力は%d

scanf("%d", &years);整数は

scanf("%lf", &savings);

double の入力は %lf を使います。
%f ではないので注意です。

確認問題(○×)

次の項目について、正しいものには○、間違っているものには×をつけてください。

① scanfで文字列を入力するとき、格納先はchar型配列を使う。
② char name[20]; に対して scanf("%20s", name); と書けば安全である。
③ scanfで文字列を入力するとき、配列名の前に & を付ける必要はない。
④ scanfの %s は空白を含む文字列も最後まで読み取れる。
⑤ scanf(" %c", &ch); のように %c の前にスペースを入れると、空白や改行を読み飛ばせる。
⑥ scanf("%lf", &x); は float 型変数 x に実数を入力できる。
⑦ scanf は変換指定に合わない入力があると、読み取れなかった文字を残したまま終了することがある。
⑧ scanf("%d%d", &a, &b); のように、複数の整数を一度に入力できる。
⑨ scanf("%s", str); では、入力が長すぎるとバッファオーバーフローの危険がある。
⑩ 文字列の末尾には終端文字 \0 が必要なので、配列サイズより1文字少なく入力制限を付けるのが基本である。

解答と解説

① ○
文字列は char の配列として扱います。scanf の %s は char配列に格納します。

② ×
配列サイズが20なら、入力制限は19が基本です。終端の \0 の分が必要なので scanf("%19s", name); が安全です。

③ ○
配列名は先頭要素のアドレスを表すので、& は不要です。scanf("%s", name); のように渡します。

④ ×
%s は空白類文字(スペース、タブ、改行)で読み取りを終了します。空白を含む入力は別の方法が必要です。

⑤ ○
" %c" のようにスペースを入れると、空白や改行を読み飛ばして次の1文字を読めます。

⑥ ×
%lf は double 用です。float なら %f を使います。

⑦ ○
変換指定に合わない入力があると、読み取れない文字がバッファに残り、次の入力に影響することがあります。

⑧ ○
scanf は複数の変換指定を並べて、一度に複数の値を読み取れます。

⑨ ○
入力長を制限しない %s は危険です。必ずフィールド幅を付けて読み取り長を制限するのが基本です。

⑩ ○
\0 が必要なので、配列サイズ N に対して入力制限は N-1 が基本です。