c-realloc()如何重新分配内存



realloc()如何重新分配malloc()首先分配的内存?

我知道在重新分配内存之前需要使用malloc(),但我不明白这应该如何工作。如果动态内存对象的大小减少了realloc(),该怎么办?在调用realloc()之后,对象的这一部分是否刚刚被擦除?


我的问题是:

  1. realloc()函数如何重新分配由malloc()创建的动态内存对象?

注意:我做了这个问答;A因为许多初学者似乎仍然对使用realloc()重新分配内存的问题感到困惑,尽管在SO上已经存在该主题的问题。对于刚接触这个话题的人来说,它们似乎有点令人困惑,但仍然不能代表realloc()的整个行为。因此,由于这些问题,IMHO,仍然不太符合我想要给出的答案,我做了自己的问答;A.

注意:以下答案中的所有引用都引用自实际的C标准ISO/IEC 9899:2018(C18)第7.22.3.4节。


首先,ISO/IEC 9899:2018中realloc()功能的概要,第7.22.3节:

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

尽管realloc()函数有其名称,但它不会"重新分配"任何内容。realloc()而不是修改内存中现存的对象。相反,它执行某种"创建(新对象)和复制数据"例程。


如果size不是0,并且ptr指向由存储器管理功能之一分配的对象(不仅仅是malloc())或指向NULL,则realloc()通常创建一个新对象并将数据从旧对象复制到新对象中。

*我说通常是,因为你不能假设内存中的新对象真的被分配了。您必须始终通过检查返回的指针是否指向NULL来检查它是否已分配。


如果新对象的大小大于旧对象,则超出旧对象大小的新对象字节具有不确定值。如果新对象比旧对象短,则会丢弃两者之间的差值。每个其他值都保留在新对象中,就像它在旧对象中一样。

在解除分配之前,新对象的内容应与旧对象的内容相同,以新大小和旧大小中较小的为准。新对象中超出旧对象大小的任何字节都具有不确定的值。


之后,如果

  • ptr而不是指向NULL的指针,是内存管理函数早些时候返回的指针,并且该指针指向的对象在调用realloc()之前尚未解除分配,

    如果ptr是一个空指针,那么realloc函数的行为与指定大小的malloc函数类似。否则,如果ptr与内存管理函数先前返回的指针不匹配,或者调用free或realloc函数释放了空间,则行为未定义。

  • size不是0

    如果大小为零并且没有为新对象分配内存,则由实现定义是否释放旧对象。如果未释放旧对象,则其值应保持不变。

  • 并且如果realloc()没有返回指向NULL的指针

    如果大小为非零并且没有为新对象分配内存,则不会释放旧对象

并且实际上只有当所有这些前提都满足时,realloc()才会释放旧对象的内存,并返回一个指针,其中包含内存中新对象的地址。

realloc函数解除分配ptr指向的旧对象,并返回一个指向具有size指定大小的新对象的指针。

如果realloc()返回指向NULL的指针,则不会创建新对象,并且旧对象在内存中的地址保持不变。


可选地,为了使"伪重新分配"行为几乎完美,在旧对象的释放完成(如果发生)后,新对象可能会被分配回存储旧对象的内存中的同一地址。

realloc函数返回一个指向新对象的指针(其值可能与指向旧对象的指针相同),如果尚未分配新对象,则返回一个空指针。

在这种情况下,realloc()中逻辑上有两个数据复制过程,一次复制到缓冲区对象,然后再复制回原始旧对象的存储位置。在realloc()的执行完成之后,释放缓冲区对象。


首先用于指向旧对象的ptr的指针不应用于返回的指针。如果对realloc()的调用语句如下所示:

ptr = realloc(ptr,size);

如果重新分配失败,通常会出现内存泄漏,因为您只是用空指针覆盖了指向旧内存的指针。如果你没有另一个指向它的指针,那么你就泄露了内存。

因此,通常最好在上使用变体

void *new_space = realloc(ptr, new_size);
if (new_space == NULL)
{
/* …handle out of memory condition… */
/* ptr is still valid and points to the previously allocated data */
return; /* Or otherwise do not continue to the following code */
}
ptr = new_space;
size = new_size;

请注意,根据我上面所说的,地址可能与呼叫realloc()之前的地址相同。


为了确保内存管理真的以这种方式进行,我们可以尝试这个实验:

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
size_t length1 = 4;
size_t length2 = 2;
int *ptr1 = malloc(sizeof(*ptr1) * length1);
if(ptr1 == NULL)
{
printf("The object could not be allocated!n");
return 1;
}  
printf("value (not address) of ptr1 before realloc(): %pn", (void *)ptr1);
ptr1 = realloc(ptr1,length2);
if(ptr1 == NULL)
{
printf("No new object allocated. Old object remains!n");
return 1;
}
printf("value (not address) of ptr1 after realloc(): %pn", (void *)ptr1);
free(ptr1);
return 0;
}

在我的尝试中,它给出了以下输出:

value (not address) of ptr1 before realloc(): 0x1db4010
value (not address) of ptr1 after realloc(): 0x1db4010

因此,在使用realloc()之后存储在ptr1中的地址与调用它之前的地址相等

附加说明:

ptrNULL指针时,realloc()充当malloc()
int *ptr = NULL;
size_t length = 4;
ptr = realloc(ptr,sizeof(*ptr) * length);

应具有与相同的效果

int *ptr;
size_t length = 4;
ptr = malloc(sizeof(*ptr) * length);

如果ptr是一个空指针,那么realloc函数的行为与指定大小的malloc函数类似。

但是,在我个人看来,您不应该首先使用realloc()来分配动态存储。我建议您始终使用malloc()或其他分配内存管理功能。它可能会给未来的读者带来一些困难。


  • 您不应该使用realloc(ptr,0)作为free(ptr)的替代来释放动态内存,因为无论旧对象是否真的被释放,它都是由实现定义的

如果大小为零并且没有为新对象分配内存,则由实现定义是否释放旧对象。如果未释放旧对象,则其值应保持不变。

始终使用free()来解除分配动态分配的对象。

相关内容

  • 没有找到相关文章

最新更新