C言語関係掲示板

過去ログ

No.1295 メモリリークについて

[戻る] [ホームページ]
No.17385

メモリリークについて
投稿者---dai(2004/10/19 09:45:01)


daiと申します。
掲題の件について教えて下さい。

ファイルからデータを読み込み、読込んだデータを動的にchar型の変数に代入しているのですが、ファイルの行数がわからないため、ダブルポインタにして1行読込むごとに「realloc()」してメモリを確保しています。

処理が終了したら「free()」でメモリを解放しているのですが、終了時にメモリリークのメッセージが表示されます。

Detected memory leaks!
Dumping objects ->
{35385} normal block at 0x0046FC30, 20 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
{35379} normal block at 0x0046FCF0, 20 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
{35367} normal block at 0x0046FA90, 20 bytes long.




メモリが正常に解放されていないようなのですが、どのように対処すればよろしいのでしょうか?

ただ、構成を「Release」に変更するとメッセージは出なくなるのですが、これは正常に解放されたとみなしていいのでしょうか?

申し訳ありませんが、ご教授、よろしくお願いします。

Visual C++6.0 WindowsXP


No.17386

Re:メモリリークについて
投稿者---tetrapod(2004/10/19 09:50:25)


>メモリが正常に解放されていないようなのですが、どのように対処すればよろしいのでしょうか?
正しく free() するようにプログラムを修正する。

>ただ、構成を「Release」に変更するとメッセージは出なくなるのですが、これは正常に解放されたとみなしていいのでしょうか?
いいえ。
Debug 構成のときはいろいろなチェックがなされる(その分遅い)のに対し、
Release 構成のときはチェックされない(その分速い)だけです。



No.17387

Re:メモリリークについて
投稿者---dai(2004/10/19 10:18:31)


daiです。
早速のご返答、ありがとうございます。

>Debug 構成のときはいろいろなチェックがなされる(その分遅い)のに対し、
>Release 構成のときはチェックされない(その分速い)だけです。
やはり、そういうことなのですね。

>正しく free() するようにプログラムを修正する。
メモリの解放処理が正しくないということになるのでしょうが、ソースを一部、公開するので、ご教授願います。

【ソース】
    CStdioFile  csfInpF;
    CStdioFile  csfOutF;
    CString  csBuf;
    CString  csCvrt;
    CString  csTmp;
    CString  csRet;
    CString  csParts;
    CString  csSymbol;
    CString  csPartsFlg;
    char        **cParam;
    int   icnt;
    int   iParam;
    int   i;
    int   j;
    int   iIniPartsCnt;


    if(!csfInpF.Open(csOpenPath,CFile::modeRead,NULL)){
        AfxMessageBox("指定されたファイルを開くことはできません",MB_ICONSTOP);
        return ID_CVRT_ERR;
    }

    icnt=0;
    csCvrt.Empty();
    cParam=(char**)malloc(sizeof(char*));

    while(csfInpF.ReadString(csBuf) != NULL){
        csCvrt=Param_Check(csBuf);
        *cParam=(char*)malloc(sizeof(char)*ID_DATA_BUFSIZE);
        strcpy(*cParam,csCvrt);
        cParam-=icnt;
        icnt++;
        cParam=(char**)realloc(cParam,sizeof(char*)*(icnt+1));
        cParam+=icnt;
    }
    csfInpF.Close();

    //データのアドレスを開始位置に戻す
    cParam-=icnt;

    //ファイル書込み用のハンドルをオープン
    if(!csfOutF.Open(csSavePath,CFile::modeWrite | CFile::modeCreate,NULL)){
        return ID_CVRT_ERR;
    }

   for(i=0;i<icnt;i++){
        csfOutF.WriteString(*cParam);
        csfOutF.WriteString("\n");
        free(*cParam);
        cParam++;
    }
    cParam-=icnt;
    free(cParam);
    csfOutF.Close();
    return 0;




No.17389

Re:メモリリークについて
投稿者---tetrapod(2004/10/19 10:34:26)


> cParam-=icnt;
> cParam=(char**)realloc(cParam,sizeof(char*)*(icnt+1));
これはダメです。
free()/realloc() には malloc() の返した値を渡す必要があります。
これでは正しく realloc()/free() されていません。

っていうか C++ なんですよね?ならば今のコードは全部書き直して
malloc/free の代わりに new/delete を使うほうがいい。
いやむしろ vector とか list とか string とか使うほうがいい。
# 全体的に malloc/realloc/free の使い方を理解してないように見受けられます。
# 俺がこんなコードのメンテする羽目になったら全面書き換えします。



No.17392

Re:メモリリークについて
投稿者---dai(2004/10/19 11:31:58)


daiです。
ご返答ありがとうございます。

>> cParam-=icnt;
>> cParam=(char**)realloc(cParam,sizeof(char*)*(icnt+1));
>これはダメです。
>free()/realloc() には malloc() の返した値を渡す必要があります。
>これでは正しく realloc()/free() されていません。
このやり方では、まずいということなのですね。realloc()の第一引数には
メモリブロックの先頭アドレスということで、先頭アドレスを指定したつもりだったのですが、修正方法はどのようにすればよいのですか?
malloc()で取得したアドレスを一次変数に確保しておき、それを指定すればいいのかなとも思い、それも試したのですが、ダメでした。


>っていうか C++ なんですよね?ならば今のコードは全部書き直して
>malloc/free の代わりに new/delete を使うほうがいい。
逆にnew()を使用する場合は今の場合だと、どのような指定方法になりますでしょうか?
もし、よろしかったら簡単なサンプルを載せていただけたら非常に助かります。

># 全体的に malloc/realloc/free の使い方を理解してないように見受けられます。
メモリについてはもっと勉強しないとダメですね…

ご教授、よろしくお願いします。



No.17398

Re:メモリリークについて
投稿者---tetrapod(2004/10/19 12:55:22)


最近は new/delete を生に書くようなことはしていないのでこんなのを。

void testfunc(std::istream& is) {
    std::string s;
    std::list<std::string> l;
    while (std::getline(is, s)) {
        l.push_back(s);
    }
    std::copy(l.begin(), l.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
}

ぜんぜん参考にならないですね。
でもこれでメモリリークも1行の長さも気にならないです。



No.17400

Re:メモリリークについて
投稿者---tetrapod(2004/10/19 13:03:20)


先の発言は標準 C++ の例としておいといて。

ポインタと配列の違いというか類似性というか、その辺の理解は大丈夫でしょうか。

int main() {
    char** p=(char**)malloc(sizeof(char*)*10);
    for (int i=0; i<10; ++i) p[i]=(char*)malloc(sizeof(char)*20);
    char** q=(char**)realloc(p, sizeof(char*)*30);
    for (int j=10; j<30; ++j) q[j]=(char*)malloc(sizeof(char)*20);
    //
    for (int k=0; k<30; ++k) free(q[k]);
    free(q);
    return 0;
}



ふつー、こんな感じで使うものです。



No.17410

Re:メモリリークについて
投稿者---dai(2004/10/19 14:22:32)


daiです。
サンプルソース、ありがとうございます。

>ポインタと配列の違いというか類似性というか、その辺の理解は大丈夫でしょうか。
>
int main() {
    char** p=(char**)malloc(sizeof(char*)*10);
    for (int i=0; i&lt;10; ++i) p[i]=(char*)malloc(sizeof(char)*20);
    char** q=(char**)realloc(p, sizeof(char*)*30);
    for (int j=10; j&lt;30; ++j) q[j]=(char*)malloc(sizeof(char)*20);
    //
    for (int k=0; k&lt;30; ++k) free(q[k]);
    free(q);
    return 0;
}


こういう使い方ができるとは知りませんでした(^^;)
原因を突き止めていった結果、ファイルから取得したデータの変換処理をしている関数でメモリの解放を忘れていたことが発覚し、それを修正したら正常に終了しました。
また、realloc()の指定も上記の例に習って修正しました。
自分のケアレスミスによって迷惑をおかけしてしまいました。
今回で、メモリの扱いに気をつけないといけないということを再認識しました。
本当に、ご教授の程、ありがとうございました。
今後ともよろしくお願いします。



No.17412

Re:メモリリークについて
投稿者---tetrapod(2004/10/19 16:21:11)


挙げたのはあくまでサンプルですのでエラーチェックのたぐいは省略してます。
実用に供する気があるならそれなりのエラーチェックを行ってください。
特に realloc まわりは注意が必要です。

p=realloc(p, newsize); // これはダメです
realloc に失敗したときに p の前の値が失われてしまうま。

q=realloc(p, newsize); // これなら OK
realloc に成功した場合には free(p); してはいけません。

C++ なら new と smart pointer を使うといい。 delete の出番は無いです。
っていうかむしろ vector か list を使うほうが簡単です。
となると既に出したサンプルみたいになっちゃいますな。



No.17413

Re:メモリリークについて
投稿者---dai(2004/10/19 16:52:22)


daiです。

>p=realloc(p, newsize); // これはダメです
>realloc に失敗したときに p の前の値が失われてしまうま。
>
>q=realloc(p, newsize); // これなら OK
>realloc に成功した場合には free(p); してはいけません。
>
今は、「p=realloc(p, newsize);」のやり方で行っています。
同一の変数で行わない方がよさそうですね。

>C++ なら new と smart pointer を使うといい。 delete の出番は無いです。
new演算子はまだ、使用したことがないのでこれから勉強したいと思います。
ちなみにnew演算子で動的にメモリを増やしていった時は既に設定されているデータは保持した形で増やされるということなのでしょうか?

>っていうかむしろ vector か list を使うほうが簡単です。
>となると既に出したサンプルみたいになっちゃいますな。
そんな方法があるとは、初めて知りました。
一歩ずつ、段階を踏んで勉強します。

本当、参考になります(^^;)



No.17390

Re:メモリリークについて
投稿者---ぽこ(2004/10/19 10:36:26)


MFCを利用してのメモリリークなら、以下のURLが参考になると思います。
http://kkkon.hp.infoseek.co.jp/tips/MFC/MemoryLeak.shtml


No.17393

Re:メモリリークについて
投稿者---dai(2004/10/19 11:46:03)


daiです。
ご返答ありがとうございます。

>MFCを利用してのメモリリークなら、以下のURLが参考になると思います。
>http://kkkon.hp.infoseek.co.jp/tips/MFC/MemoryLeak.shtml
参考にさせていただきます。ありがとうございます。


No.17391

Re:メモリリークについて
投稿者---REE(2004/10/19 11:17:02)


ぱっと見では、直接の原因は分かりませんでしたが、
とりあえず、cParamをicntだけ変化させる代わりに、
cParam[icnt]の表記を使った方がよいです。