- ベストアンサー
printf関数の振る舞い?に関して
皆様、こんにちは。 C言語のデバッグに関して詳しい方どなたか教えてください。 現在、静的SQLを使ったC言語でのプログラム開発をおこなっております。DBはIBM DB2で、マシンはAS400、言語は C/400です。最初はPCで、Microsoft Visual Stadio .NET 2003で開発して、やはりPC上のIBM DB2にアクセスしてテストしておりました。 問題のCのソースはステップ数が巨大(Cで5,000ステップ)なため、printf関数をとこどどころに入れてテストしておりました(プリコンパイル→コンパイルの作業がある為?にデバッガーは使えなかった為)。 全てのテストが完了したので、ソース上からprintf関数を全て削除して、実行させてみました。 ところがprintf文が入って、正しい結果を出力していたころのプログラムとは思えないほどの、エラーが続出しました。 まず、メモリーエラーと思われるエラー、永久ループ、重複レコード出力エラーなどです。これらは今までと、同じデータで実行したので、全く同じロジックを通るはずなのですが、まるで別人のような振る舞いをしまます。 以前、デバッガーにはデバッガー自身が動くためにメモリーを整理して実行するため、このような事がおきるとは聞いておりましたが、ただのprintf関数で、このような事が起きる事があるのでしょうか? 本プログラムはstrcpyやstrcmp関数が多用されていて、NULL -STOPが正しくセットされていないのではかと、現在調査・改修しているところです。 皆様、このような現象って何が考えられるでしょうか? 宜しくお願いいたします。
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
printf文を削除してロジックが変わってしまったと思われます。 例えば if (判定式) printf();/*デバッグ*/ 次のステートメント となっているのを、printf文を削除すると、判定式が偽の時に「次のステートメント」が実行されません。 また、printf文の中の引数に副作用を含む場合、単純に削除してはいけません。 例えば for (i=0,ptr=buf;i<1000;) { *ptr = XXX; printf("%d=[%d] ",i,*ptr++);/*デバッグ*/ } *ptr = '\0'; となっているのを、printf文を削除するとptrがインクリメントされません。 このようなロジックの変化が起きる可能性、副作用の有無がある可能性に影響されないようにデバッグ行を入れる場合は、直接printf文を使用してはいけません。 デバッグ行は debug_printf("%d=[%d] ",i,*ptr++); のように書いておき、これらの呼び出し側は削除したり変更したりしてはいけません。 デバッグプリントをする、しないの切り替えは、以下のようなデバッグプリントルーチンを用意して切り替えます。 int debug_printf(char *fmt,...) { int cnt = 0; #ifdef DEBUG va_list argptr; va_start(argptr, fmt); cnt = vprintf(fmt, argptr); va_end(argptr); #endif return(cnt); } こうしておき、識別子「DEBUG」を定義するか定義しないかで、デバッグプリントをするかしないか切り替えます。 デバッグプリントを無しにした際、ゼロを返すだけの関数を呼び、完成時のコードに無駄が出る事になりますが、printf文を削除して罠にハマるよりはマシです。 「例えそれがデバッグ行だろうとも、動いてるソースは書き換えない」がプログラミングの鉄則です。
その他の回答 (4)
- jacta
- ベストアンサー率26% (845/3158)
printfが入っていたときはたまたま動いていただけ、あるいはprintfの実引数で必要な副作用が起こっていたかのどちらかだと思います。 一度、どこかで int dummy() { return 1; } のような関数を作り、 #define printf dummy として実行してみてください。また、 #define printf (void) も試してみてください。(printfの返却値を使っている場合は、もう一工夫する必要があります) 特に、printfを(void)に置き換えてまともに動作するのであれば、実引数の副作用に依存している可能性大です。 そうでない場合、未初期化の変数がないかどうか、マルチスレッド環境の場合には最適化されると問題が起こる記述がないか調べてみてください。 それでも直らない場合はかなり深刻ですので、実際のソースコードを見てみないとどうしようもないと思います。
お礼
jactaさん、回答ありがとうございました。 とても参考になりました。#define printf (void)みたいな やりかたもあるんですね。試してみて、問題の切り分けをしてみます。 色々と参考になりました。 また何かありましたら、宜しくお願いいたします。
- stiffels
- ベストアンサー率34% (25/72)
VC++6の話ですが、参考までに コンパイル方法をデバッグからリリースにすると 同じようにメモリ関係でおかしなエラーが起きることがあります。 Borlandのコンパイラでは起こりませんが MicroSoftのコンパイラはリリースビルドの時だけ(オプションが違うのかも) 32ビットで割り切れないメモリをmallocなどすると 32ビットで割り切れるように切り捨ててしまうことがあり その為、char型やBYTEを含む構造体などをmallocして使おうとすると 同じようなおかしなエラーが出たりします。 そのため、32ビットで割り切れないメモリの割り当てを行う場合 多めに割り当てるか、パディングなどをすると回避することができます。
お礼
回答ありがとうございます。 コンパイル方法の変更でこのような違いがあるんですね。 今回の問題には直接的に関係はなさそうですが、今後の 開発の参考にさせていただきます。 貴重なご意見ありがとうございましたm(__)m
- venzou
- ベストアンサー率71% (311/435)
http://okwave.jp/qa2624408.html 似たような質問が過去にありました。参考にして下さい。
お礼
回答ありがとうございます。 #過去ログとても参考になりました! ありがとうございました。
- okg00
- ベストアンサー率39% (1322/3338)
・printf関数を除去する過程で余計な部分を削除した ・削除対象とするソースが古いものだった
お礼
回答ありがとうございます。 >・printf関数を除去する過程で余計な部分を削除した 多分に考えられますが、削除前のバックアップソースも ありますので、比較してみたところ、問題なかったんですよ。 >・削除対象とするソースが古いものだった 最新のソースです(^^;;; でも、そう思えるくらい、動きがおかしいのです。 ありがとうございました。 またお気づきの点があればご教授くださいませ。
お礼
chie65536さん、回答ありがとうございました。 とても参考になりました。 #ifdef DEBUG #endif という記述、見たことはありましたが、実際にこのように するのですね。 副作用の有無に関しては、ただ変数の中身を直接出しているだけなので ご指摘の問題はないかと思われます。週明けにでも試してみたいと思います。 また、その後ソースを調べていて、変数の初期化抜け、nullストップの不在、 strncpyの桁数オーバーに因る他の領域の破壊などがいくつか見つかりました。 printf()があるときは騒がなかったバグが、あったようです(^^;;; 「プログラミングの鉄則」も忘れずにこれからも取り組んでいきたいと 思います。また何かありましたら、宜しくお願いいたします。 また、貴重な時間にご覧になっていただいた方々ありがとうございました。