C言語 標準関数 | 応用 | サンプル

標準関数
構文
応用

管理人

プライバシーポリシー


書式

#include <stdio.h>

int scanf( const char *format , ... );

■戻り値:
標準入力stdinからの入力をformat文字列に従った変換を行い、format文字列より後ろに指定された引数に代入して、その代入した個数を返す。
変換に失敗した場合はエラーとしてEOFを返す。
format文字列とは%sとか%dを指す。

説明

標準入力stdin(キーボード)から入力された値をプログラム変数に取り込み、その取り込んだ個数を返す。取り込む先のプログラム変数の指定は、上記の書式で指定された第2引数以降「...」部分に任意の個数を指定することができる。

但し、「...」指定時にはアドレス指定でなくてはならない。取り込む変数型はformat文字列にformat指定子として指定するが、取り込む変数の型と順序及び個数を一致させなくてはならない。(一致させた方が無用なトラブルを避けられる)

標準入力から入力を複数個指定するには、入力毎にリターンキーを押下(\nによる区切り)するか、半角空白またはtabで区切って(1行に)複数の値を入力(後にリターンキーを押下)するかである。(リターンキー及び、半角空白またはtabでの複合区切りでも構わない)

尚、取り込んだ変数の末尾にNULL(\0)を補完するのはformat指定子が%sの場合である。
最後に本関数scanf(fscanf含む)についての問題点を記述した。
この問題点により、入力データが制限されているか個人使用でなければ本関数の使用は推奨しない。

サンプル(数値の取り込み)

#include <stdio.h>

main() {
  int ret;
  int idata;

  /* 標準入力stdinからデータを取り込み */
  ret = scanf( "%d" , &idata );
  if( ret == EOF ) {
    printf( "scanfでエラー\n" );
    return -1;
  }

  /* 取り込んだデータを表示 */
  printf( "idata=%d\n" , idata );
  printf( "ret=%d\n" , ret );

  return 0;
}

実行結果(最初の1行は標準入力からの入力である)

123
idata=123
ret=1

サンプル(文字列の取り込み)

#include <stdio.h>

main() {
  int ret;
  char sdata[24];

  /* 標準入力stdinからデータを取り込み */
  ret = scanf( "%s" , sdata );
  if( ret == EOF ) {
    printf( "scanfでエラー\n" );
    return -1;
  }

  /* 取り込んだデータを表示 */
  printf( "sdata=%s\n" , sdata );
  printf( "ret=%d\n" , ret );

  return 0;
}

実行結果(最初の1行は標準入力からの入力である)

abc
sdata=abc
ret=1

%s指定時のバッファオーバーフローについて

上記のサンプルを見ると分かるように、読み込んだデータを格納する領域は24バイトである。この24バイトには自動で付与されるNULLも含まれているので実質23バイトである。

もし、標準入力からこれを超える長さが入力された場合はオーバーフローしてしまう。これを利用した攻撃が世間で言われるバッファオーバーフローであり、領域を幾ら増やしてもそれ以上の長さを入力されたら対処できない。この場合の回避策として、%sを%23sとして23バイトまでを読み込むようにすれば良い。

但し、この方法では%23sとすることで実際には標準入力からは23バイトを超える長さを入力しても23バイトしか読み取っておらず、23バイトを超える部分は標準入力に残されているので注意が必要である。

サンプル(バッファオーバーフローを回避)

#include <stdio.h>

main() {
  int ret;
  char sdata[24];

  /* 標準入力stdinからデータを取り込み */
  ret = scanf( "%23s" , sdata );
  if( ret == EOF ) {
    printf( "scanfでエラー\n" );
    return -1;
  }

  /* 取り込んだデータを表示 */
  printf( "sdata=%s\n" , sdata );
  printf( "ret=%d\n" , ret );

  return 0;
}

実行結果(最初の1行は標準入力からの入力である)

abcdefghijklmnopqrstuvwxyz
sdata=abcdefghijklmnopqrstuvw
ret=1

%23s%23sなどの複数指定の場合

上の説明で%23sを指定して23文字を超える入力をすると、23文字を越える部分が残されると述べた。

もし、%23s%23sなどと複数指定するとこの残された部分が2つ目の%23sに影響してしまい、この影響を排除しようとすると、23文字以下の長さを入力した場合に対処できなくなってしまう。逆に23文字以下の入力に対処すると、23文字を超えた場合に対処できなくなる。結果としてこれを「完全に」回避することはscanfの仕様上、不可能であると思われる。

詳細な説明は長くなるので割愛するが、scanfで複数の値を取り出すには完全さを放棄して仕様上の妥協を受け入れるか、scanf以外の方法を模索するのが合理的であると言わざるを得ない。


Copyright © 2008-2015 http://hitorilife.com All Rights Reserved.