注意:以下回答中的所有引用都摘自实际 C 标准 ISO/IEC 9899:2018(C18)第 7.22.3.4 节。
首先,ISO/IEC 9899:2018 第 7.22.3 节中 realloc()
函数的简介:
#include <stdlib.h>
void *realloc(void *ptr, size_t size);
尽管其名称为realloc(),但该函数并不“重新分配”任何东西。realloc()不会修改内存中的现有对象。相反,它执行某种“创建(新对象)并将数据复制”的例程。
如果size不为0且ptr指向由内存管理函数之一(不仅限于malloc())分配的对象,或者指向NULL,则realloc()通常会创建一个新对象,并将数据从旧对象复制到新对象中。
我之所以说“通常”,是因为您不能假设在内存中真正分配了一个新对象。必须始终检查返回的指针是否指向NULL来确定是否已分配新对象。
如果新对象的大小大于旧对象,则超出旧对象大小的新对象的字节具有不确定的值。如果新对象比旧对象短,则差异中的值会被丢弃。新对象中的每个其他值都与旧对象中的原始值相同。
之后,如果:
- ptr不是指向NULL的指针,并且是先前由内存管理函数返回的指针,该指针指向的对象在调用realloc()之前没有被释放,
- size不为0,
- 如果realloc()未返回指向NULL的指针,则确实可以分配新对象,
只有当所有这些前提条件都满足时,realloc()才会释放旧对象的内存并返回一个指向在内存中的新对象的地址的指针。
如果realloc()
返回一个指向NULL
的指针,则不会创建新对象,并且旧对象仍然以其在内存中的地址保持不变。
为了使“准重新分配”行为几乎完美,可选地,在释放旧对象后(如果发生),新对象可能会分配回与旧对象存储在同一内存地址处。
realloc函数返回指向新对象的指针(可能与指向旧对象的指针相同),或者如果未分配新对象,则返回空指针。
在这种情况下,在 realloc()
中有逻辑上的两个数据复制过程,一次是进入缓冲区对象,稍后再回到原始旧对象存储的地方。执行realloc()
后缓冲区对象将被释放。
用于指向旧对象的ptr
指针不应用于返回的指针。如果调用realloc()
的语句如下:
ptr = realloc(ptr,size);
如果重新分配内存失败,通常是由于你刚刚用空指针覆盖了旧内存的指针,那么你通常会有一个内存泄漏问题。如果没有其他指向它的指针,就会出现内存泄漏。
因此,通常最好使用以下变体:
void *new_space = realloc(ptr, new_size);
if (new_space == NULL)
{
return;
}
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(): %p\n", (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(): %p\n", (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是一个null指针,realloc函数将为指定大小的内存行为类似于malloc函数。但是在我个人的看法中,你不应该首先使用realloc()来分配动态存储。相反,我建议您始终使用malloc()或其他分配内存管理函数。这可能会给未来的读者带来一些困难。
你不应该使用realloc(ptr,0)作为替代free(ptr)来释放动态内存,因为是否真正释放旧对象是实现定义的。
当大小为零并且新对象的内存未被分配时,实现定义了旧对象是否被释放。如果旧对象没有被释放,它的值将不变。请始终使用free()来释放动态分配的对象。
ptr = realloc(NULL, 0);
来初始化指针是完全有效的,并且非常常见。请参见相关代码:https://code.woboq.org/userspace/glibc/malloc/malloc.c.html#3158。 - Marco Bonellirealloc()
来分配对象,尝试寻找具有明显更合适的函数的相对初始化的动态内存对象。因此,我只是为了不引起任何不必要的问题而写下了这个。由于标准说它是可能和允许的,当然任何人都可以这样做。但我只是不建议这样做。 - RobertS supports Monica Celliomalloc/realloc
的各种细微差别方面做得很好,但使用realloc
进行初始分配也是可以的。这样做没有任何问题。当然,我理解你的想法,即最好先使用malloc/calloc
进行分配,然后再调用realloc
(对于新的C程序员来说可能更容易理解),但是所有这些都不能使使用realloc
进行初始分配成为错误或不好的选择。(好的编辑--现在清楚了) - David C. Rankin