okuの日記: scanf 書式文字列考
株式会社きじねこ は [迷信] scanf ではバッファオーバーランを防げない より:
私は scanf(3) の書式文字列を全部把握していたつもりだったのですが、
char s[10];
scanf("%9s%*[^\n]%*c", s);
のように [〜] の中に改行を埋め込んで scanf に改行を読み取らせてしまう方法は今まで思いつきませんでした。 ナイーブな CSV ファイルを読むのに1バイトずつカンマをチェックするコードを見たときには「sscanf の書式文字列の [〜] 使えばいいんじゃないの?」とは言ってきたのですが。
いや〜、世の中には頭のいい人がいるもんだなぁ、と思って手元の環境 (GCC-4.1.2 + glibc-2.7) で試してみたところ...
みごとに改行がバッファに残りました。
orz
元記事が間違っているのか、glibc-2.7 のバグなのかは未検証です。
というか、JIS X 3010 や POSIX の manpage を読んでも、[〜]中の「\n」がどう解釈されるべきなのかは良く分かりませんでした。
差し当たり、scanf(3) に改行を読ませようとする方法には移植性がない、とする他なさそうです。
追記:
ちなみに私自身は「scanf は改行の解釈が頭痛の種だから使うな」派です (でした)。
上記の記事の手法がうまくいけば改宗しても良かったのですが。
まあ、もう少し正確に言うと「混ぜるな危険」「使っても良いけど改行のことで質問に来る前に C-FAQ 読め」派ですかね。
正直言って、scanf の改行の解釈は仕樣バグと言っても過言ではないのではないかとすら思います。 後、厄介な「仕樣」は printf 族の書式文字列との微妙な違いでしょうか。 特に、printf の「*」に当たる物がないのが嫌なところであったりします。 例えば、上記のコードで「9」の代わりに「sizeof s」を使って渡す簡単な方法がありません (書式文字列を動的に生成すればできなくはありませんが、それを「簡単」という人は多分いないと思います)。
とまあ、これだけだとただのボヤキになってしまうので、上記のコードが意図通りに振舞う処理系とそうでない処理系を列挙しようと色々な環境で試してみたところ...
-
GCC-4.1.2 + glibc-2.7 (上述) … NG -
VC++ 2005 Express Edition … NG -
Borland C++ 5.5.1 … NG -
Turbo C++ (Borland C++ 5.8.2) … NG -
DigitalMars C++ 8.50 … NG -
GCC-3.4.4 (i686-pc-cygwin / i686-pc-mingw32) … NG
ううむ...
後、自力で試せそうなのは OpenWatcom くらいですか。
更に追記:
上記で「意図した通りに動かない」と書いたのはテストコードのバグでした。 というわけで「scanf でもいいじゃん」派に改宗します。 :-)
scanf 書式文字列考 More ログイン