realloc()的正确使用方法

28

根据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                     
}

假设重新分配的指针只指向不同的内存块而不是相同的内存块,这样就足够了吗?因为如果这个假设不成立,并且realloc返回指向ptr原始内存块的地址,然后执行free(ptr)(由于评论中的原因),那么该内存块将被清除,程序会崩溃。

我是否应该再加入另一个条件来比较ptr和ptr1的相等性,并排除执行free(ptr)语句的可能性?

6个回答

37

在正常情况下,不要对原始指针调用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
}

1
@user3163420 真的吗?在我的 Mac 上 man realloc 的说明是:“……如果没有足够的空间来扩大 ptr 所指向的内存分配,realloc() 将创建一个新的分配,将尽可能多的旧数据复制到新的分配中,释放旧的分配,并返回指向已分配内存的指针。……” - meaning-matters
5
不要对mallocrealloc的结果进行强制类型转换。 - emlai
我觉得需要支持 zenith 的建议。如果没有强制类型转换,你可能会多得到两个 +1。 - autistic
1
呵呵...类型转换不仅难看,而且可能在某些晦涩的系统上引起问题,也没有任何作用...但你保留它们是为了保持代码的原始性。现在引入了空格,使其不那么难看,这也没有任何功能目的,但不会破坏任何东西(不像类型转换,它会)。我的观点是,如果您允许添加空格以使代码风格上有吸引力,那么您是否也允许删除不必要的类型转换以使代码更具可移植性,并且风格上有吸引力?我不打算编辑,但如果我编辑了,您会还原吗? - autistic
1
/me 哭着说“Uncle!”。移除了铸件。 - Aaron S. Kurland
显示剩余2条评论

5

根据以下良好的评论,将修复内容作为编辑应用。

阅读这个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;

请注意添加的 else if(ptr != tmp),它排除了情况 1,因为您不希望调用 free(ptr),因为ptrtmp引用相同的位置。另外,仅仅出于安全考虑,我确保将NULL赋给tmp 以避免出现任何悬空指针问题,同时tmp在作用域中。

1
你没有遵守“前一个指针值将变得无用”的部分。在 ptr != tmp 的情况下,free(ptr) 是错误的。 - glglgl
1
同意@glglgl的观点,这个答案是危险的,因为在情况2中不能调用free(ptr)。 - user7761803
好的呼叫,已应用为编辑,并对你们两个进行评论点赞。 - Keeler
@Keeler +1 为将 tmp 指针设置为 NULL。我曾经因在函数内部更改 realloc 的指针为静态指针而受到严重打击。这导致后续的 realloc 调用中出现了段错误(在函数调用之间释放了内存),因为现在指针保留了其旧值(悬空)。花了我一段时间才弄清楚... 我有一个使用 realloc 而不是 malloc 的习惯,但必须保持警惕,确保第一个 realloc 调用(没有先前的 malloc)获得一个 NULL 指针。 - GermanNerd
我认为在这里使用exit(1)会更加合适。 - siride

2

OP: ...可能与ptr不同,或者如果请求失败,则为NULL。
A:并非总是如此。如果count为0,则NULL可能会被合法地返回(不是失败)。

OP:仅假定重新分配的指针指向不同的内存块而不是同一块内存,是否足够?
A:不是。

OP:我应该再加一个条件来比较ptr和ptr1的相等性,并排除执行free(ptr)语句吗?
A:不用。

如果realloc()返回NULL(且count不为0),则ptr的值仍然有效,指向未调整大小的数据。是否释放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;
}

1
如果 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);
}

那里有一个问题:如果ptr是NULL,free(ptr)就没有意义了。 - SirDarius
嗯,那确实有道理。而且程序即将退出,所以不释放也没问题 :) 修复它 :) - Paulo Bu
1
这是一个实现细节,不适用于一般情况:“如果realloc有足够的空间来扩展ptr指向的实际内存块,则它将返回相同的地址。”按大小分配(例如OpenBSD的omalloc)的实现不太可能返回原始指针,除非原始大小和新大小匹配。 - R.. GitHub STOP HELPING ICE
对于在malloc()的返回值上添加强制类型转换的注释,可以加上+1 - ryyker
2
ptr=realloc(ptr,count*sizeof(int)); 这段代码有问题。当 realloc 返回 NULL(它不是一个地址,因为它不指向任何对象)时,你会泄漏旧对象的内存。The OpenGroup manual 中写道: "如果空间无法分配,则对象将保持不变。"C 标准也规定:"如果无法为新对象分配内存,则不释放旧对象,其值保持不变。" - autistic
@autistic 不是出了问题。如果获取内存失败(realloc 返回 NULL)导致程序终止(如 Paulo Bu 的示例),则不会发生内存泄漏。当无法获得内存是程序运行的终端问题时,使用 realloc 的方式是正确的。 - GermanNerd

0

如果realloc成功,您不应该释放原始指针。 如果realloc失败是否释放该指针取决于您特定应用程序的需求; 如果您绝对无法继续而需要额外的内存,则这将是一个致命错误,您将释放任何保留的存储并退出。 另一方面,如果您仍然可以继续(也许执行不同的操作并希望稍后会有内存可用),则可能希望保留该内存并稍后尝试另一个realloc

章节和诗句:

7.22.3.5 The realloc function

Synopsis

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

Description

2 The realloc function deallocates the old object pointed to by ptr and returns a pointer to a new object that has the size specified by size. The contents of the new object shall be the same as that of the old object prior to deallocation, up to the lesser of the new and old sizes. Any bytes in the new object beyond the size of the old object have indeterminate values.

3 If ptr is a null pointer, the realloc function behaves like the malloc function for the specified size. Otherwise, if ptr does not match a pointer earlier returned by a memory management function, or if the space has been deallocated by a call to the free or realloc function, the behavior is undefined. If memory for the new object cannot be allocated, the old object is not deallocated and its value is unchanged.

Returns

4 The realloc function returns a pointer to the new object (which may have the same value as a pointer to the old object), or a null pointer if the new object could not be allocated.

强调添加。请注意第4条款;返回的指针可能与您的原始指针相同。


0
如果realloc移动了您的数据,它将在幕后为您释放旧指针。我没有C11标准的副本,但在C99标准中保证了这一点。

C11标准草案是n1570.pdf,您可以通过谷歌搜索找到它。我发现使用链接进行引用很有帮助(例如,[点击此链接](http://port70.net/~nsz/c/c11/n1570.html#7.22.3.5p1)将带您进入n1570.html的'realloc'部分,该部分是从n1570.pdf转换而来的)。 - autistic

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接