これも根が深い迷信です。この迷信を根拠にscanf関数は使うべきではないという人も大勢います。おそらくこういうことでしょう。
0 1 2 3 |
char s[10]; scanf("%s", s); |
確かにこれでは、ユーザーが10文字以上入力した時点で未定義の動作を引き起こしてしまいます。しかし、これは書式指定が不適切なために発生する脆弱性であって、scanf関数の問題ではありません。
バッファオーバーランを回避するには次のようにします。
0 1 2 3 |
char s[10]; scanf("%9s", s); |
これで差し当たっての問題はなくなりました。
さて、熱心な迷信の信者は、何とかしてscanf関数の名誉回復を阻むために、巧みに論点のすり替えを行いながら、scanf関数を貶めようとすることでしょう。
例えば、上記のように文字列を読み込めばバッファオーバーランは防げるかもしれないが、格納されなかった文字、あるいは改行文字がバッファに残ってしまい、後で悪さをすると。
そんな場合は、こうすれば解決します。
0 1 2 3 |
char s[10]; scanf("%9s%*[^\n]%*c", s); |
ただし、これは10文字以上文字列を入力した場合を想定しています。実際にこの記述を行った場合、9文字以下しか入力しなければ改行文字が残ります。9文字以下にも対応するには、例えば下記のようにします。
0 1 2 3 4 5 6 7 8 9 10 11 |
char s[10]; char c; if (scanf("%9s%c", s, &c;) == 2) { if (c != '\n') { ungetc(c, stdin); scanf("%*[^\n]%*c"); } } |
面倒ですが、fgets関数を用いても、
0 1 2 3 4 5 6 7 8 |
char s[10]; char c; fgets(s, 10, stdin); if (s[strlen(s)-1] != '\n') { while (getchar() != '\n'); } |
のようにせざるを得ませんから、大差はありません。行中の余計な文字を読み飛ばすだけですので、scanf関数を使う場合でも
0 1 2 3 4 5 6 7 |
char s[10]; char c; if (scanf("%9s%c", s, &c;) == 2) { while (c != '\n') c = getchar(); } |
でもよいでしょう。なお、改行無しで EOF が発生する場合や I/O エラーに備えるためには、もう少しエラーチェックが必要ですが、今回は割愛します。
scanf系の関数は、文字列を読み込むことに限ればfgets関数なんかよりずっと柔軟な処理ができます。要は、書式指定を完全に把握する気があるかどうかの問題です。
ところで、先ほど「文字列を読み込むことに限れば」と敢えて書きました。というのも、文字列を読み込む以外、例えば整数や実浮動小数点数を読み込む場合、scanf系関数には意外に知られていない問題点があるからです。これは、fscanf関数でもsscanf関数でも同じことがいえます。
scanf系の関数では、整数や実浮動小数点数を読み込む際にオーバーフローが発生しても検知することができません。実引数で指定した格納先の型で入力した数値を表現できない場合の動作は未定義になります。
scanf関数でバッファオーバーランを防げないと盲信している人は、数値の入力には脊椎反射的にfgets関数とsscanf関数で置き換えるという選択をすることが少なくありません。しかし、先に書いたような問題があるため、そうした方法は安全ではありません。ですから、
0 1 2 3 4 5 6 7 8 9 |
char s[100]; int x; fgets(s, 100, stdin); if (sscanf("%d", &x) < 1) { /* エラー処理 */ } |
ではなく
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
char s[100]; long t; int x; char *endptr; fgets(s, 100, stdin); errno = 0; t = strtol(s, &endptr, 10); if (errno != 0 || *endptr != '\n' || (t < INT_MIN || INT_MAX < t)) { /* エラー処理 */ } x = t; |
とした方がよいのです。
ちなみに、C11から導入されたscanf_s系の関数を使ったとしても、本質的な問題解決にはならないことを付け加えておきます。
最後の方はかなり横道にそれてしまいましたが、scanf系関数に関連することですのでご容赦ください。バッファオーバーランに関しては、IPAのセキュア・プログラミング講座の記事も参考にしてください。