00/08/21いきなりだが、VC++を使っているとしよう。Debugビルドでうまく動いていたのに、いざ製品版にしようと思ってReleaseビルドにするとメモリエラーで落ちることがある。非常に困ったちゃんである。いうことが無いように、assertマクロ(yaneSDK2ndではWARNINGマクロ)を至るところに入れて、NULLチェックや、範囲外チェックを日ごろから行なっておかなければならないのだが、毎日が月曜日だもんでそうも言ってられない。確実な手法は、どこまで実行されているのか記録を取ることである。yaneSDK2ndでは次のようなマクロを用意している。Err.Out("%s(%d)",__FILE__,__LINE__);現在のファイル名と実行行を出力するマクロである。至るところに、このTRACEを仕掛けてどこまでが実行されているかを調べるのである。俗に言うロギング(logging)である。言うものの、そういう悠長なことも言ってられない。スマートにデバッグできないのか?せめて、どの関数でメモリエラーが起きているのか調べられないのか?方法はある。一つは、プロジェクト→設定→リンク→デバッグで、Mapファイルを生成することである。これで、どの関数がどのアドレスに位置しているのかがわかる。これでエラーアドレスから関数を特定出来る。一つの方法は、同じくプロジェクト→設定→リンク→デバッグで、デバッグ情報を出力することである。しておけば、実行中にメモリエラーが起こったならば、即デバッグに入ることが出来る。この二つの方法では、メモリエラーが遅れて発生するような場合、あるいはメモリエラーが発生しない場合などは役に立たないかも知れない。そこで、もっと直感的なデバッグ手法を考えてみよう。根本に立ち戻って、DebugビルドとReleaseビルドの最大の違いは何か?やねうらおも、こんなところにおいそれと書けるほど良くは知らないのだが(笑)、とりあえず初期化されていない変数の値が違う。Debugビルドであれば、0xcdcdcdcdで初期化されているものの、Releaseでは不定である。0のときもあれば、デタラメな数字であることもある。そのため、Releaseビルドにして、不安定な動作をしているならば、それは変数の初期化忘れが第一候補として考えられる。クラスのコンストラクタですべてのメンバ変数の初期化は正しく行なわれているかぐらいはチェックしたほうが良いだろう。二つ目の違いとして、実経験に基づくものだが、データ列のデリミタとしてNULLを使っているような場合、デバッグビルドの場合たいていデリミタのNULLを忘れていても、どこかにあるNULLで停止することが多い。ところが、Releaseビルドだと、行くところまで行きついてしまって、メモリエラーで落ちることが多い。←最後がNULLを保証したい。};が不可能というのもこのようなバグを誘発する原因として挙げられるかも知れない。型の正当性チェックとともに、structの正当性チェックという考え方も今後、流行るかも知れない。(全然、流行らないかも知れない^^)三つ目は、メモリのガーベジ(?)による違いである。deleteしたオブジェクトに対してアクセスを行なった場合、もちろん動作は保証されないわけだが、DebugとReleaseで挙動が異なる(メモリエラーの発生するタイミングが異なる)ことがある。詳しくはよくわからない^^しかし、最近、まともなOOP言語を見ていると、C++のように他のオブジェクトを指すポインタをオブジェクト消失後も持ち続けることが出来るというのは、いいのか〜とか思わないでもない。これでは、よほど気をつけて管理しないと、バグの原因となる。スマートポインタがコストがかかると言うならば、たとえばオブジェクト消失時に、何らかの方法でそのオブジェクトを指すポインタはすべてNULLにして、かつNULLポインタによる関数呼び出しは行なわないようにしてあればいいような気もする。そもそも、クラスのインスタンスのメンバ関数呼び出しは、オブジェクトに対してメッセージを送ることであって、オブジェクトが存在しない(受け手が存在しない)ならば、メッセージは当然届かなくて良いと思うのだが...。零さんから、こんなメールが来てました。埋まってる状態)に戻すようです。することによってメモリリークを検出できるようにしてます。したあとも、上書きされてなければアクセスしてもヘーキなんです。