- - PR -
C の localtime() の引数は混乱を招く。
投稿者 | 投稿内容 | ||||||||
---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2004-09-10 18:58
auto 変数はスタック領域にとられるが、そこには関数を呼び出したときのリターンアドレスもかき込まれます。 明示的に初期化されないauto変数はその変数が割り中たられた場所にたまたま書かれた値が入ったままになります。 実態の無いポインタ型の変数に値を代入するということは、はたまたまそこに書かれた数字をアドレスとして参照する場所に値をかき込む亊になります。 たまたまのアドレスが書き込みがゆるされない領域の場合にはその時点でセグメンテーションエラーが出ます。 たまたまのアドレスがこれ以前にスタック領域内に記録されたリターンアドレスとかポインタ変数とかの場所だった場合、それらの値を参照したときにおかしなことになったりセグメンテーションエラーになります。 まだ使われていない領域立った場合は何もおきなかったように見えます。(プログラムを実行していく間に破壊される畏れは有ります) しかし多分プログラムは問題のポインタ型変数を確保する前にスタックを決まった手順で使用されていると思いますので、初期化していないポインタ型変数は同じ値を示してしまいます。 多くの方が異なる環境やオプションで試されていますが、環境やオプションによりスタックへの変数の積まれ方が変わるので、値は変わって来ているのではないでしょうか。 | ||||||||
|
投稿日時: 2004-09-10 19:21
>受け取り側でポインタを設定される可能性があるから、値設定されていないポインタを
>引数にとってもエラーにならないんですね。 なるほど。 人に言われて初めて気が付きました。 引数として渡して、中で実体のアドレスを受け取って抜ける、パターンもありか・・・ ちぃにぃさんのソースをパクってちょっと無茶な事しましたが。。
$ gcc -Wall -O -o nullpo nullpo.c nullpo.c: 関数 `main' 内: nullpo.c:22: 警告: `p' はこの関数内で初期化されずに使用される可能性があります $ ./nullpo main 0x42130a14 print_unassigned_pointer=0x804956c (C) print_unassigned_pointer_with_one_args=0x80496c0 (C) print_unassigned_pointer=0x804956c (C) $ gcc -Wall -o nullpo nullpo.c $ ./nullpo main 0x40015360 print_unassigned_pointer=0x80495ac (C) print_unassigned_pointer_with_one_args=0x8049700 (C) print_unassigned_pointer=0x80495ac (C) セグメンテーション違反です $ gcc -Wall --omit-frame-pointer -o nullpo nullpo.c $ ./nullpo main 0x40015360 print_unassigned_pointer=0x80495ac (C) print_unassigned_pointer_with_one_args=0x8049700 (C) print_unassigned_pointer=0x80495ac (C) セグメンテーション違反です $ コンパイル・エラー無い、あってもワーニング程度で、見事に領域破壊。。。勿論、 Solaris8 ではこの時点でセグメンテーション違反無し。 RedHatで、この、main() の unsigned char *p; を NULL で初期化するだけで結果が 違ごてきます。 main() の unsigned char *p; を unsigned char *p = NULL; に変更。 $ gcc -Wall -O -o nullpo nullpo.c $ ./nullpo main (nil) print_unassigned_pointer=0x804958c (C) print_unassigned_pointer_with_one_args=0x80496e0 (C) print_unassigned_pointer=0x804958c (C) $ gcc -Wall -o nullpo nullpo.c $ ./nullpo main (nil) print_unassigned_pointer=0x80495ac (C) print_unassigned_pointer_with_one_args=0x8049700 (C) print_unassigned_pointer=0x80495ac (C) $ gcc -Wall --omit-frame-pointer -o nullpo nullpo.c $ ./nullpo main (nil) print_unassigned_pointer=0x80495cc (C) print_unassigned_pointer_with_one_args=0x8049720 (C) print_unassigned_pointer=0x80495cc (C) $ エラー全く無し、実行結果異常無し。。。 NULL って言う実体が、あるのか無いのか・・・ | ||||||||
|
投稿日時: 2004-09-10 19:37
>C言語は上記ですから、lint もまとめて考えては?
Linux の splint も結構イケますよね? lint 系、今後は積極的に使うようにします。>MMX氏 >多くの方が異なる環境やオプションで試されていますが、環境やオプションによりスタック >への変数の積まれ方が変わるので、値は変わって来ているのではないでしょうか。 変わりまくりです。。コンパイラ依存なんでしょうけど・・・もし実行環境が一つしか 無い場合、間違いに気づくまでに相当な時間を費やす場合もあるかと思います。 まぁ根気の要る作業ではあります。>Toshi氏 | ||||||||
|
投稿日時: 2004-09-10 20:12
今回のは Electric Fence (mallocを置き換える) じゃ検出できませんが、
Purify plus とか Bounds Cheker (Windowsのみ?) とかだと、 どうなのでしょうかね‥‥(さほど C/C++してないので、使ったことないのです) # と書いておくと、そのうち @IT に SRA さんや CompuWare さんの広告や # セミナーの案内や入門記事が載るんではないかな、と期待してみたり。 | ||||||||
|
投稿日時: 2004-09-10 22:10
ついで。不正なアドレスに書き込んだときの挙動については、
たとえば、こんなので確認できます。 ----------------------------------- segv.c
Cygwinのgcc で試すと、こうなりました: $ gcc -g -Wall -o segv -O segv.c $ gdb ./segv GNU gdb 2003-09-20-cvs (cygwin-special) (略) (gdb) r Starting program: /home/username/tmp/segv.exe Program received signal SIGSEGV, Segmentation fault. main (argc=1, argv=0xa052af8) at segv.c:7 7 *((volatile char*)0x00001000) = 2; (gdb) q The program is running. Exit anyway? (y or n) y | ||||||||
|
投稿日時: 2004-09-11 01:02
なんだか「C入門講座」になってますが、その流れとは関係のないところで...
# にしても、初心者にいきなり volatile ですか
おっと、そこまで突っ込んだ話だったのですね。 誤解してました。失礼致しました。 とりあえず、K&R 第2版 には「time_t は時間を表す算術型であり」 と書かれていますね。
う〜ん... http://lagendra.s.kanazawa-u.ac.jp/ogurisu/manuals/c/C-faq/C-faq-11.html に書いてあるあたりでしょうか。 JIS の規格書は大学や研究機関の図書館に置いてあったりしますね。 | ||||||||
|
投稿日時: 2004-09-11 01:07
>受け取り側でポインタを設定される可能性があるから、値設定されていないポインタを
>引数にとってもエラーにならないんですね。 正確に言うと受け取り側でポインタは設定されません。 あと、エラーにならなくてもワーニングがでれば問題ないと思いますが 基本的に「不具合の原因になる可能性の警告」なのですから まぁワーニングをすべて潰しても、「状況によって結果が違う」不具合を 未然に潰すことはできないですからコブラさんが危惧していることは もっともだとは思います。 さんざんそれでひどい目にもあいましたので。 その分小手先のデバック技術はつきましたが(それがいいかは別として) なので、コブラさんが例としてだしたソースですがfreeをコメントすると NULLに初期化しない場合でも動くと思います。 あとデバック文は関数に入る前、関数内、関数からでた後の3セットで いれるといいと思います。 | ||||||||
|
投稿日時: 2004-09-11 11:04
ゆうじゅんさん、確認しました。何か、年季を感じます。
未初期化のポインタ変数があっても、 $ gcc -Wall -O -o nullpo nullpo.c このコンパイルオプションの時以外は、警告すら出ませんでした・・・ 仮引数と実引数の番地が違うのは当然でしょうけど、 free(p); する前に malloc(); で確保した領域を呼び出し先で p に代入しても、 関数から復帰したらもう未初期化のポインタ変数の番地に戻ってもて、free(p)したい 領域を free できんのですな。。。 最上位のスタックフレームで未初期化のポインタ変数を free(p) しようとする (プ 未初期化のポインタ変数は、仮引数として渡す前に実体を伴う番地で初期化してやらないと、 Call by reference 使こても実引数になった時点で既に違う番地を指してるので、 関数から復帰後の番地の変更が伴わない・・・ やっぱり、こうするか、
こうするべきでしたか。。。
という事は、 >正確に言うと受け取り側でポインタは設定されません。 これは正しい訳ですな。 しかし、SRA と言えば、昔は unix 絡みのイベントには必ずブースを設けて 新製品もバンバンアピールしとりましたが、 Looking Glass は X.Desktop に 食われて、 Purify は ・・・ 大昔にセミナーご招待とかありましたが、最近は どうなんですかね。PostgreSQL の教育で忙しいんかな。。。 [ メッセージ編集済み 編集者: コブラ 編集日時 2004-09-11 11:10 ] |