文字列の入力
文字列の scanf には & を付けない
名前を決め打ちで書くのではなく、キーボードから受け取れるようにします。書式は %s です。
char name[20];
scanf("%19s", name);数値のときは scanf("%d", &score); と & を付けましたが、char 配列のときは & を付けません。&name と書いても動いてしまう場合がありますが、正しい書き方ではないので付けないでください。なぜ配列だけ扱いが違うのかは第7章で回収します。今は「文字列の入力に & は要らない」と覚えておけば大丈夫です。
%s は空白で切れる
%s が読むのは、空白や改行が来るまでの1かたまりです。
入力 Sato Taro
結果 name には Sato だけが入る残った Taro は入力に置いたままなので、次の scanf がそれを読みます。「フルネームを入れてください」と表示して %s で受けると、姓しか入らないうえに、次の入力がずれる、という二重の事故になります。
この章では名前を空白なしの1語で受け取る前提にします。空白を含む1行をまるごと読む方法は、fgets を学ぶ第8章で扱います。
幅を指定しないと箱があふれる
もうひとつ、こちらのほうが深刻な問題があります。
char name[20];
scanf("%s", name); // 危ない%s は、入力が何文字来ても止まらずに書き込み続けます。20個の箱に30文字入力されたら、配列の外にはみ出して、隣にある別の変数を壊します。これがバッファオーバーフローと呼ばれる、C でいちばん有名な事故です。実行が止まらないまま値だけ壊れることもあり、原因を突き止めるのが非常に大変です。
防ぎ方は、%s に 読み込む最大文字数 を書くことです。
scanf("%19s", name); // 最大19文字まで書く数は、配列の大きさそのものではなく 大きさマイナス1 です。終端記号のぶんを1個空けておく必要があるからです。char name[20] なら %19s、char name[32] なら %31s になります。前回の strcpy と同じで、「中身の長さと箱の大きさは別物」という話がここでも効いています。
配列の大きさと書式の数字が離れた場所にあると、片方だけ直す事故が起きます。宣言のすぐそばに書く、という習慣で減らせます。
空白は区切りとして働く
%s が空白で止まるのは欠点ばかりではありません。空白を区切りとして使えば、名前と点数を続けて読むことができます。
char name[20];
int score;
scanf("%19s", name);
scanf("%d", &score);Sato 85 と1行で入力しても、Sato と改行して 85 と入力しても、どちらも同じように読めます。%s も %d も、値の手前にある空白や改行を読み飛ばしてから中身を読み始めるからです。第2章で数値を2つ続けて読んだときと、まったく同じしくみが働いています。
では、名前と点数を入力して受け取ってみましょう。
要件
- 名前は char name[20] に、幅を指定した %s で読み込む
- 点数は int で読み込む
- Satoさんの点数は85点です の形で表示する
- 名前は4文字です の形で文字数を表示する
入出力例
main("Sato 85") → "Satoさんの点数は85点です
名前は4文字です"
main("Yamada", "72") → "Yamadaさんの点数は72点です
名前は6文字です"