K.Maebashi's BBS

ご自由に書き込んでください。雑談も可。
テスト書き込みの類はテスト用掲示板にどうぞ

[日付順表示] [日付順インデックス] [スレッド順インデックス]

新規投稿 | 開設者ホームページへ戻る | ヘルプ

[227] インタプリタとコンパイラ
投稿者:kon
2007/02/20 02:13:25

いつも楽しく拝見しています。 久しぶりに投稿します。konです。 質問なんですが、 共通機能でログを作成する機能をPerlで作成する事になりました。 その際、ログファイルに情報を1行ずつ書くのですが 必ず最後にスペースが入って改行される現象がありました。 原因は my $string = sprintf("%s %s %s\n", $day, $message); 上記のコードで、2番目の%sの後のスペースが表示されて、3番目の%sが引数のエラー にならない事が分かりました。 Cなどのコンパイラ型の言語なら、コンパイルエラーになりますが Perlのようなインタプリタ型の言語は、なぜエラーにならないのでしょうか? (strictはいれてますが、ワーニングにもなりません) Perlのマスターが言うには、”Perlはアバウトだからね”と意味不明(論理的でない?) 回答が返ってきました。私も、あまりPerlをやっていないのでとても 気持ちが悪いです。
[この投稿を含むスレッドを表示] [この投稿を削除]
[229] Re:インタプリタとコンパイラ
投稿者:774RR
2007/02/20 02:13:25

>my $string = sprintf("%s %s %s\n", $day, $message); perl はぜんぜん知りませんが、この sprintf が C の sprintf と同じ機能と仮定して: C/C++ では上記コードのエラー検出はされません。 sprintf は「可変個引数」な関数です。 固定な引数=必ず必要な引数、が無いとエラーになりますが、 可変個数部分=有るか無いかは状況次第、が過不足していてもエラー検出することはできません。 原理的に不可能。 # GNU CC では printf 系関数のフォーマット引数が文字列リテラルな場合に限り、 # 個数や形の不一致を検出してくれますが、一般的には無理。
[この投稿を含むスレッドを表示] [この投稿を削除]
[230] Re:インタプリタとコンパイラ
投稿者:kon
2007/02/20 02:13:25

御指摘ありがとうございます。 >sprintf は「可変個引数」な関数です。 >固定な引数=必ず必要な引数、が無いとエラーになりますが、 >可変個数部分=有るか無いかは状況次第、が過不足していてもエラー検出することはできません。 >原理的に不可能。 申し訳ありません。すっかり忘れていました。 (前橋さんの本などで、話題になっていたのに・・・) printf系の関数は実引数が不足しているときの動作は未定義でしたね。 >C/C++ では上記コードのエラー検出はされません。 確認せずに記述してしまいました。(VC++でも検出しませんでした) 大変お騒がせ致しました。 (件名のインタプリタとコンパイラとは一切関係ありませんでした (;_;) )
[この投稿を含むスレッドを表示] [この投稿を削除]
[232] Re:インタプリタとコンパイラ
投稿者:iwa
2007/02/20 02:13:25

>Perlのようなインタプリタ型の言語は、なぜエラーにならないのでしょうか? インタプリタだから、ではなくて、perlの言語仕様的な問題です。 perlの場合、関数の引数は全て配列(@_)として扱われます。なので、この場合は、 一つ目の%sには$_[0]が、二つ目の%sには$_[1]が、三つ目の%sには$_[2]が入ります。 このとき、Cあたりだと配列長を超えたらそのままバッファオーバーラン、Javaだと 例外が投げられますが、perlの場合は読むときはundefが返り、書くときは自動で 配列長が拡大されます。 今回の場合は、$_[2]を読もうとして返ってきたundefが文字列化されて空文字列 扱いになったわけです。
[この投稿を含むスレッドを表示] [この投稿を削除]
[234] Re:インタプリタとコンパイラ
投稿者:(ぱ)
2007/02/20 02:13:25

Perlに詳しいわけではありませんが、故あって最近Perlのソースを眺めたりも してるので、ちょっと調べてみました。 >perlの場合、関数の引数は全て配列(@_)として扱われます。なので、この場合は、 >一つ目の%sには$_[0]が、二つ目の%sには$_[1]が、三つ目の%sには$_[2]が入ります。 Perlで記述された関数についてはその通りでしょうが、今回のケースのsprintfは Perl組み込みの関数ですから、Cで書かれた実装部分に引数の数が渡っているかどうかが 問題では? で、5.8.6のソースから探したところ、どうもsv.cのPerl_sv_vcatpvfnが sprintfの実装のように見えます(1000行近い巨大関数。sprintfじゃあ しょうがないですが)。 void Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV **svargs, I32 svmax, bool *maybe_tainted) doop.cのPerl_do_printf()から追跡すると、このsvmaxが、引数の数のように見えます。 printfかましてminiperlを再構築し、確認しましたが、実際に引数の数が渡って きているようです。 が、この関数の中でsvmaxをどう使っているか見てみると、 if (svix < svmax) { というように「svmaxを超えてないか?」というチェックは何箇所かでしてますが、 上記の箇所でもelse節はなかったりするので、そのために何も起きないんじゃ ないでしょうか。
[この投稿を含むスレッドを表示] [この投稿を削除]