mallocの後のreallocについて

[戻る]
なおぞう 2016/01/26(火) 17:58:09
よろしくお願いします。
VS2013 VC++ Win32コンソールアプリで開発しています。

複数回ファイルを読み込み、メモリ領域にセットするのですが、読み込むファイルの大き
さがそれぞれ違うという事で、mallocで適宜ファイルサイズ分メモリを取ろうと考えまし
たが、何回もmalloc、freeを繰り返すとゴミがたまるということで、mallocで取った領域
をreallocで再利用する方法でやってみようと思いました。

テストプログラムとして以下のような感じで考えましたが、reallocのところで、
「test.exeによってブレークポイントが発生しました」というエラーが出ます。
ネットで調べると、ヒープ領域を壊しているとでるエラーだと書いてあります。
どこが悪いのかわからなくて困っています。ご指摘お願いします。
(memcpy( buf+(i*size), cbuf, size);//バッファを埋めていく をコメントアウトすれ
ばreallocはできましたが)

また、メモリ領域の上手な使いまわし方などがありましたらご教授いただけると助かります。


    char cbuf[] = "TESTDASTFSETSESTDFSDA";
    int size = strlen(cbuf);
  
    char* buf = (char*)malloc(2000);//サイズ2000でmallocする

    if (buf == NULL){
        printf("Faild malloc \n");
        return -1;
    }
    memset(buf, '\0', 2000);
    for (int i = 0; i*size < 2000; i++){
        memcpy( buf+(i*size), cbuf, size);//バッファを埋めていく
    }
    printf(buf);
    printf("\n");

    memset(buf, '\0', 2000);
    //reallocしてみる
    char* tmp;
    tmp = (char*)realloc(buf, 200); //<<<<----ここでエラー
    if (tmp == NULL){
        printf("Faild realloc \n");
        return -1;
        }else{
        buf = tmp;
    }
    memset(buf, '\0', 200);
    for (int i = 0; i*size < 200; i++){
        memcpy(buf + (i*size), cbuf, size);
    }
    printf(buf);
    printf("\n");


よろしくお願いします。

YuO [E-Mail] 2016/01/26(火) 18:15:05
size は21,2000 / 21 = 95 あまり5。
なので,iが21の時,i * size = 1995なので, 11行目のi * size < 2000を満たします。
この結果,直後のmemcpyではbuf[1995]からbuf[1995 + 21 - 1]つまりbuf[2015]までのデータを
書き換えます。
bufは2000バイトしか確保していないため,バッファオーバーランが発生しています。

11行目の条件式は,「末尾が書き込めること」であるため,
i * size + size - 1 < 2000
としないといけません。


末尾で200に対して行っている部分も同じことが言えます。

なおぞう 2016/01/26(火) 19:27:16
>YuO 様

ありがとうございます。
forループのbreakが条件が正しく定義できてなかったのですね。
恥ずかしいミスです…。


int idx = 0;
while(1)
{
 if (2000 -( idx*size) < size) {break;}
  memcpy(buf + (idx*size), cbuf, size);//バッファを埋めていく
  idx++;
}
のほうがよさそうでしょうか。
これなら、大丈夫……ですよね。


メモリ領域の使いまわしとしてはreallocを使うのは問題ない事がわかって助かりました。
またよろしくお願い致します。

瀬戸っぷ [E-Mail] 2016/01/27(水) 10:19:09
>何回もmalloc、freeを繰り返すとゴミがたまるということで、mallocで取った領域
>をreallocで再利用する方法でやってみようと思いました。

ランタイム内でmallocなどが利用されていない。
# ヒープの利用は自分だけ。
ということでなければ、断片化は避けられないかも知れません。

realloc()の第1引数と戻り値が同じだった場合はメモリブロックのサイズ変更で済んでいる。
ということになりますが。
# 縮小する場合は成功する可能性は高いでしょう。
# 拡大す場合は後続メモリに空きがないと失敗する(新たに確保してコピーして古い方が
解放される。新たに確保できない場合はNULL返却)可能性が高くなるでしょう。

なおぞう 2016/01/27(水) 13:10:02
>瀬戸っぷ様

サイズ拡大で、 reallocでNULLが返ってきたら、もうメモリ領域をとることは不可能と
判断するべきなのでしょうか?

char * ptr1 = (char*)malloc(2000);
char * tmp ;

if( (tmp = realloc(ptr1,4000) == NULL){
 //メモリ領域が取れなかったので処理続行は不可能??
}
else
{
  ptr1 = tmp;
}

瀬戸っぷ [E-Mail] 2016/01/27(水) 14:17:19
>サイズ拡大で、 reallocでNULLが返ってきたら、もうメモリ領域をとることは不可能と
>判断するべきなのでしょうか?

概ねそういうことになるかと思われます。

malloc()やrealloc()でメモリ取れなくなったときにどうするか…ってのは問題だったりし
ますけどね。
# ぬるぽ避ける為にエラーにはするけど、回復処理はあんまり考えないなぁ。
# リスト構造とかでメモリ確保や解放繰り返すコード書いたことはあるけども。

なおぞう 2016/01/27(水) 16:00:36
>瀬戸っぷ様
返答ありがとうございます。

mallocはメモリ解放してもすぐに解放されて再利用できるわけじゃないという話も聞いた
ことがあるのですが…。

難しいですね。
とりあえず、NULLが返ってきたらエラーメッセージ出して処理を終了させようと思います
が…回復処理してとか言われたらどうしたらよいのやら。
実行環境のメモリが大きければ安全って考えてよいのでしょうか。

瀬戸っぷ [E-Mail] 2016/01/27(水) 16:15:59
>実行環境のメモリが大きければ安全って考えてよいのでしょうか。

OSがそのプロセスに割り当てるヒープ領域のサイズ次第…じゃないですかね。
そこまで大量にメモリ使う事はありませんので実際のところは不明ですが。

なおぞう 2016/01/27(水) 16:31:36
[[解決]]
メモリが足りなくて何度も停止するようだったら「ヒープ領域を大きく取り直す」で対
応。としようと思います。

ありがとうございました。

古いスレッドには追加発言できません。