web-dev-qa-db-ja.com

realloc()の適切な使用法

Man reallocから:realloc()関数は、新しく割り当てられたメモリへのポインタを返します。これは、あらゆる種類の変数に適切にアラインされ、多分 ptrとは異なります。要求が失敗した場合はNULLになります。

したがって、このコードスニペットでは:

ptr = (int *) malloc(sizeof(int));
ptr1 = (int *) realloc(ptr, count * sizeof(int));
if(ptr1 == NULL){           //reallocated pointer ptr1
    printf("Exiting!!\n");
    free(ptr);
    exit(0);
}else{
    free(ptr);          //to deallocate the previous memory block pointed by ptr so as not to leave orphaned blocks of memory when ptr=ptr1 executes and ptr moves on to another block
    ptr = ptr1;         //deallocation using free has been done assuming that ptr and ptr1 do not point to the same address                     
}

再割り当てされたポインターが同じブロックではなく、別のメモリブロックを指していると仮定するだけで十分ですか?仮定がfalseになり、reallocがptrが指す元のメモリブロックのアドレスを返し、次にfree(ptr)を返すためです(コメントで与えられた理由で)実行すると、メモリブロックが消去され、プログラムが異常終了します。 ptrとptr1の同等性を比較し、free(ptr)ステートメントの実行を除外する別の条件を設定する必要がありますか?

22
user3163420

ハッピーパスの元のptrでfree()を呼び出さないでください。基本的にrealloc()がそれを行います。

ptr = malloc(sizeof(int));
ptr1 = realloc(ptr, count * sizeof(int));
if (ptr1 == NULL) // reallocated pointer ptr1
{       
    printf("\nExiting!!");
    free(ptr);
    exit(0);
}
else
{
    ptr = ptr1;           // the reallocation succeeded, we can overwrite our original pointer now
}
24

以下の良いコメントに基づいて、修正を編集として適用します。

このcomp.lang.cの質問 を読むと、3つのケースが明らかになります。

  1. 「それができるとき、それはあなたがそれを渡したのと同じポインターをあなたに返すだけです。」
  2. 「しかし、十分な連続したスペースを見つけるためにメモリの他の部分に移動する必要がある場合、別のポインタを返します(そして、以前のポインタ値は使用できなくなります)。」
  3. reallocが十分なスペースをまったく見つけられない場合、nullポインターを返し、前の領域を割り当てたままにします。」

これは直接コードに変換できます:

_int* ptr = (int*)malloc(sizeof(int));
int* tmp = (int*)realloc(ptr, count * sizeof(int));
if(tmp == NULL)
{
    // Case 3, clean up then terminate.
    free(ptr);
    exit(0);
}
else if(tmp == ptr)
{
    // Case 1: They point to the same place, so technically we can get away with
    // doing nothing.
    // Just to be safe, I'll assign NULL to tmp to avoid a dangling pointer.
    tmp = NULL;
}
else
{
    // Case 2: Now tmp is a different chunk of memory.
    ptr = tmp;
    tmp = NULL;
}
_

したがって、考えれば、投稿したコードは(ほぼ)正常です。上記のコードは次のように単純化されます。

_int* ptr = (int*)malloc(sizeof(int));
int* tmp = (int*)realloc(ptr, count * sizeof(int));
if(tmp == NULL)
{
    // Case 3.
    free(ptr);
    exit(0);
}
else if(ptr != tmp)
{
    ptr = tmp;
}
// Eliminate dangling pointer.
tmp = NULL;
_

ptrtmpは同じ場所を参照するため、else if(ptr != tmp)を呼び出したくないケース1を除外する追加のfree(ptr)に注意してください。また、安全のために、NULLがスコープ内にあるときのぶら下がりポインタの問題を回避するために、tmptmpに割り当ててください。

3
Keeler

OP:...はptrとは異なる場合があります。要求が失敗した場合はNULLになります。
A:常にではありません。 NULLが0の場合、countは正当に返される可能性があります(失敗ではありません)。

OP:再割り当てされたポインターが同じブロックではなく、別のメモリブロックを指していると想定するだけで十分です。
A:いいえ

OP:ptrとptr1の同等性を比較し、free(ptr)ステートメントの実行を除外する別の条件を設定する必要がありますか?
A:いいえ。

realloc()NULLを返した場合(およびcountが0でない場合)、ptrの値は引き続き有効であり、サイズ変更されていないデータを指しています。 free(ptr)かどうかは、目標によって異なります。

realloc()NULLを返さない場合、free(ptr)を行わない場合、すべて解放されます。

例: https://codereview.stackexchange.com/questions/36662/critique-of-realloc-wrapper

#include <assert.h>
#include <stdlib.h>

int ReallocAndTest(char **Buf, size_t NewSize) {
  assert(Buf);
  void *NewBuf = realloc(*Buf, NewSize);
  if ((NewBuf == NULL) && (NewSize > 0)) {
    return 1;  // return failure
  }
  *Buf = NewBuf;
  return 0;
}

reallocが指す実際のメモリチャンクを拡張するのに十分なスペースがある場合、ptrは同じアドレスをptrに返します。それ以外の場合は、データを新しいチャンクに移動し、古いチャンクを解放します。 ptr1ptrと異なることに依存することはできません。プログラムは未定義で動作します。

reallocが別のアドレスを返す場合、最初に古いアドレスの割り当てを解除するので、自分で行う必要はありません。

ところで、malloc/realloc :)の戻り値をキャストしないでください。コードは次のようになります。

ptr=malloc(sizeof(int));
ptr=realloc(ptr,count*sizeof(int));
if(ptr==NULL)
{   
    // error!    
    printf("\nExiting!!");
    // no need to free, the process is exiting :)
    exit(0);
}
1
Paulo Bu

reallocがデータを移動する場合、バックグラウンドで古いポインタが解放されます。 C11標準のコピーはありませんが、C99標準では保証されています。

0
tabstop

freeが成功した場合は、元のポインタreallocしないでくださいfreeが失敗した場合にそのポインタをreallocするかどうかは、特定のアプリケーションのニーズによって異なります。その追加メモリがないと絶対に続行できない場合、これは致命的なエラーとなり、保持されているストレージの割り当てを解除して終了します。 OTOH、それでも続行できる場合(おそらく別の操作を実行し、メモリが後で利用可能になることを期待している場合)、おそらくそのメモリを保持し、後で別のreallocを試行する必要があります。

章と節

7.22.3.5 realloc関数

あらすじ

1
     #include <stdlib.h>
     void *realloc(void *ptr, size_t size);

説明

2 realloc関数 古いオブジェクトの割り当てを解除しますptrによってポイントされ、sizeによって指定されたサイズを持つ新しいオブジェクトへのポインターを返します。新しいオブジェクトの内容は、割り当て解除前の古いオブジェクトの内容と同じで、新しいサイズと古いサイズの小さい方までです。古いオブジェクトのサイズを超える新しいオブジェクトのバイトには、不確定な値があります。

3 ptrがNULLポインターの場合、realloc関数は、指定されたサイズのmalloc関数のように動作します。それ以外の場合、ptrがメモリ管理関数によって以前に返されたポインタと一致しない場合、または領域がfreeまたはreallocの呼び出しによって割り当て解除された場合、動作は未定義です。 新しいオブジェクトのメモリを割り当てることができない場合、古いオブジェクトは割り当て解除されず、その値は変更されません。

戻り値

4 realloc関数は、新しいオブジェクトへのポインター(古いオブジェクトへのポインターと同じ値を持つ場合があります)を返します。新しいオブジェクトを割り当てることができなかった場合は、nullポインターを返します。

強調が追加されました。条項4に注意してください。返されるポインタは、元のポインタと同じである可能性があります。

0
John Bode