ファイル読み込み時にメモリリーク


クロウ  2009-05-05 02:40:22  No: 70102

こんにちわ。

前回は大変お世話になりました、クロウです。

boost::asioの方は実装できまして、次にファイルのアーカイブ化に着手したのですが、アーカイブ化したファイル読み込み時にメモリリークが発生して困っています。

〜Main〜
CArchiveLoader* Archive;
Archive=new CArchiveLoader();
Archive->Load("imgdat.dat");

〜Archive〜
bool CArchiveLoader::Load(string name) 
{
  DWORD nbytes;
  HANDLE h=CreateFile(
    name.c_str(), GENERIC_READ, FILE_SHARE_READ, 
    NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  if (h==INVALID_HANDLE_VALUE) return false;
  DWORD size=GetFileSize(h, NULL);
  char* data=new char[size];
  if (WorkSize<size) 
  {
    if (Work) delete[] Work;
    Work=new char[size];
    WorkSize=size;
  }
  ReadFile(h, Work, size, &nbytes, NULL);
  CloseHandle(h);
  Rand.Init(Password);
  for (DWORD i=0, n=size-3; i<n; i+=4)
  {
    *(DWORD*)(Work+i)^=Rand.Int32();
  }
  DWORD sum=0;
  for (DWORD i=4, n=size-3; i<n; i+=4) 
  {
    sum+=*(DWORD*)(Work+i);
  }
  if (sum!=*(DWORD*)Work) return false;
  Entry.clear();
  char* p=Work+sizeof(DWORD);
  DWORD num_entries=*(DWORD*)p;
  p+=sizeof(DWORD);
  for (DWORD i=0; i<num_entries; i++) 
  {
    ARCHIVE_ENTRY e;
    e.Name=p;
    p+=e.Name.length()+1;
    e.Size=*(DWORD*)p;
    p+=sizeof(DWORD);
    e.Offset=*(DWORD*)p;
    p+=sizeof(DWORD);
    e.Data=Work+e.Offset;
    Entry.push_back(e);
  }
  return true;
}
〜エラーメッセージ〜

Detected memory leaks!
Dumping objects ->
{767} normal block at 0x00A76EC8, 32 bytes long.
 Data: <imgdat\     yL.png> 61 69 6D 67 5C 97 EC 96 B2 8A 79 4C 2E 70 6E 67 
{765} normal block at 0x00A75F08, 32 bytes long.
 Data: <imgdat\     {L.png> 61 69 6D 67 5C 97 EC 96 B2 93 7B 4C 2E 70 6E 67 
{763} normal block at 0x00A75EA8, 32 bytes long.
 Data: <imgdat\      L.png> 61 69 6D 67 5C 97 EC 96 B2 8A EC 4C 2E 70 6E 67 

〜〜〜
多分どこか妙な所でメモリリークしているのだと思うのですが、どうすればいいのかわかりません。

イマイチどうすればよいのかわからないので、わかる方がいらっしゃればお願いします。


tetrapod  2009-05-05 07:53:58  No: 70103

ソース読む気になれないのでぱっと見で。
new[] してるのに delete[] してないのが原因。
きっちり delete[] しておくんなまし。

ないしは new/delete せずに vector<char> 等に直すもよし。

でもさー
無節操にファイルの全データをメモリに読んでるっぽいけど
DVD-R ISO Image みたいな巨大ファイルが指定されたらとか、考えてある?


クロウ  2009-05-05 16:45:27  No: 70104

その後変更してみましたが、状況的には変わりません。
〜〜
  char* data=new char[size];
  if (WorkSize<size) 
  {
    if (Work) delete[] Work;
    Work=new char[size];
    WorkSize=size;
  }
  ReadFile(h, Work, size, &nbytes, NULL);
  delete [] data;
〜〜
この部分ではメモリリークは発生していないようなので、多分原因は別の箇所かなと思っているのですが……。


tetrapod  2009-05-05 16:58:48  No: 70105

ほかのところだと思うんなら、この部分挙げてもしょうがないじゃん・・・

new と delete は常にペアで存在するべき。
new CArchiveLoader したのを delete してるかどうかチェック。

っていうか Work にせよ CArchiveLoader にせよ new する必要があるの?
単純に局所変数にすれば delete 忘れもなくなってよいはずだが。
普通に MFC の CArchive を使う際には局所変数にして使う。
CArchive を new/delete するコードなんか見たことないよ。

CArchiveLoader と CArchive は違うんだろうけどさ・・


rin  2009-05-05 22:43:11  No: 70106

Workとdataの2種類を new[]してますが
Workはdelete[]してないですね
Workを使う前に前回のメモリを確認してるようだけど
・WorkがLoaderクラスのメンバ変数なら毎回新しいので前回のを消せない
・グローバルとかなら、アプリを終了させるときにちゃんと消しているか?

あと、dataって使ってないような・・・


クロウ  2009-05-05 23:10:19  No: 70107

rinさん、tetrapodさん>ありがとうございます。

取り合えず、自分でvector<char>Work(size)にしてみたのですが、今度はメモリリークではなく読み込み中にアクセス違反が発生しました。

後、CArchiveをヘッダ部分でCArchiveLoaderにしているので基本的にはCArchiveです。

〜ヘッダ部分〜

class CArchiveLoader : public CArchive {
protected:
  char* Work;
  DWORD WorkSize;
public:
  CArchiveLoader();
  ~CArchiveLoader();
  bool Load(string name);
};

〜〜


rin  2009-05-08 07:51:45  No: 70108

ARCHIVE_ENTRY などはあまり見かけないワードですが
継承元クラスのCArchiveはMFCのCArchiveですか?

ARCHIVE_ENTRY をMSDNで検索したり
ARCHIVE_ENTRYとCArchiveを合わせてグーグルで検索しても
うまくひっかからないので・・・


クロウ  2009-05-08 18:42:44  No: 70109

CArchiveはMFCの物です。


rin  2009-05-09 03:54:44  No: 70110

ARCHIVE_ENTRYやEntryはMFCのCArchive関連には無いようですが・・・?


クロウ  2009-05-09 08:13:07  No: 70111

〜〜
struct ARCHIVE_ENTRY {
  string Name;
  DWORD Size, Offset;
  char* Data;
};
〜〜

……となってましたです。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加