realloc()
如何重新分配malloc()
首先分配的内存?
我知道在重新分配内存之前需要使用malloc()
,但我不明白这应该如何工作。如果动态内存对象的大小减少了realloc()
,该怎么办?在调用realloc()
之后,对象的这一部分是否刚刚被擦除?
我的问题是:
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中的地址与调用它之前的地址相等
附加说明:
当ptr
是NULL
指针时,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()
来解除分配动态分配的对象。