1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

scanfに注意

Last updated at Posted at 2018-05-01

scanf を利用してデータを入力する際の注意

迷える子羊さんから、プログラムがちゃんと動かないと相談されたので、それをメモとして記述しておく

課題: 整数の入力値を2つ受け取り、処理Aを実行して結果を出力する

# include <stdio.h>
int main(){
    int a, b;
    scanf("%d, %d", &a, &b);
    /* Procedure A */
}

発生した問題: Aの処理結果が期待しているものと違う

実行手順

$ gcc test.c
$ ./a.out
1 2
<期待と異なる結果>

原因は入力データのフォーマットがscanfが要求しているものと違うからなのだけれど、ちょっと見ただけではわかりにくい

正しい入力データの場合

$ ./a.out
1, 2
<期待通りの結果>

カンマ","が入力データに有るか無いかで結果が異なる
異なるのは当然として、scanf はフォーマット文字列と入力をどのように処理しているのか?

scanfの説明としてIBMのページを参照すると次のように記述されている

The scanf() function reads format-string from left to right. Characters outside of format specifications are expected to match the sequence of characters in stdin; the matched characters in stdin are scanned but not stored. If a character in stdin conflicts with format-string, scanf() ends. The conflicting character is left in stdin as if it had not been read.

最後の行に注目!!
If a character in stdin conflicts with format-string, scanf() ends. The conflicting character is left in stdin as if it had not been read.

つまりscarfの文字列が"%d, %d"で入力が "1 2"の場合には

  • a に"1"が入る
  • "2"以降はカンマが一致しないので読み込まれない
    • 変数 b は不定
      • エラーも発生しない

そして、出力結果が誤ったものとなってしまう

この事を確かめるために、初期化前の値とscanf後の値を表示してみる

プログラムを変更

# include <stdio.h>
int main(){
    int a, b;
    printf("Before scanf: %d, %d\n", a, b);
    scanf("%d, %d", &a, &b);
    printf("After scanf: %d, %d\n", a, b);
    /* Procedure A */
}

プログラムを再コンパイルして誤ったフォーマットでデータを入力

$ gcc test.c
$ ./a.out
Before scanf: 137630162, 32766
1 2
After scanf: 1, 32766
<期待と異なる結果>

変数 b はscanf実行前の値がそのまま

正しいフォーマットでデータを入力

$ ./a.out
Before scanf: 237597138, 32766
1, 2
After scanf: 1, 2
<期待通りの結果>

scanf はCの初学者には辛い仕様となっている

だから、何かの防衛策を取る方が良いだろう

  • scanf で取得した値をユーザーに対するフィードバックとしてprintfで表示する
  • 変数を絶対に利用しない数値で初期化しておいて、scanf後に変数が所定の値となっているか判定する
    • 例えば 正の整数が必要な変数を負の値で初期化しておく
  • scanf の戻り値が適切な値(想定する入力データ数)となっているか確認する: shiracamusさんのアドバイス
    • 適切な値が読み取れるまでscanf をループする
    • ただし、bufferをクリアしないとループから抜けられないという事態が生じる場合がある(次のscanfで条件を満たさなかった位置から読み取りが開始されるので。)
    • bufferをクリアの例: char c; while((c = getchar()) != '\n' && c != EOF);
    • 初心者向けではないかも

scanf()の戻り値について

The scanf() function returns the number of fields that were successfully converted and assigned. The return value does not include fields that were read but not assigned.
The return value is EOF for an attempt to read at end-of-file if no conversion was performed. A return value of 0 means that no fields were assigned.

という事で、scanf を利用の際は慎重に:smile:

1
1
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?