昔作ったソフトの改造をしていたら、突然「Critical error detected c0000374」というエラーが出て止まるようになった。実行継続して例外を処理させてみたら、
「ハンドルされない例外が 0x00007FFB1E01E6FC (ntdll.dll) で発生しました(伏せ字.exe 内): 0xC0000374: ヒープは壊れています。 (パラメーター:0x00007FFB1E0722B0)。」
なんてダイアログが出てきた。ヒープ破損!?メモリ関係でやっちゃったかなあ・・・。とりあえず、stdafx.hに
#include <crtdbg.h>
を追加。そして、プログラムが停止したあたりに
_ASSERT(_CrtCheckMemory());
を仕込み、ヒープを破壊した箇所を絞り込む。・・・?なんでこんなコードで破壊が起きているんだ?
ちょっとだけコード解説。このコードは、CImageクラスの画像をOpenCVのMatクラスにコピーしている。最初の数行で、それぞれに対し同一サイズの画像用メモリを確保し、memcpyでコピーしている。GetPitch()でmemcpyの仕方を場合分けしているのは、CImage型の変数が有する画像イメージへの生ポインタを取得するGetBits()関数が2通りのポインタを返すためだ。
CImageは、画像左上を原点とするフォーマットと左下を原点とするフォーマットをサポートする。GetPitch()やGetBits()は、ユーザーがその違いを気にしなくても済むよう、左下原点の場合はPitchとして負の値を、GetBitsの結果として左下の点に相当するメモリアドレスを返す。これにより、たとえばforループを書くときに場合分けをせずに済むようになる。しかし、この機能は今回のコードでは邪魔で、それぞれの場合用に書き分ける必要が生じる。それが、GetPitch()でmemcpyの仕方を場合分けしている理由だ。
さて、本題。同一サイズの画像をコピーしているだけなのに、なぜヒープが壊れるのだ?・・・もしかして、画像サイズは同一でも、メモリサイズは同一ではない?試しに、1ラインあたりのメモリサイズを見てみよう。CImageもcv::Matも、構造体内にデータとして持っていたはず・・・
- CImage
img.m_nPitch : -408 - cv::Mat
*tmp.step.p: 405
ビンゴでした。1ラインあたりのメモリサイズが異なっていました。CImage::GetPitch()のドキュメントにも、「The pitch can also include additional memory, reserved for the bitmap.」なんて書いてありました。これを直せば、治るかな?