通过显示的特定代码,最初使用空指针没有问题。
如果变量ptr
未初始化——没有设置为 0 或 NULL——那么任何realloc()
使用它的调用都是危险的;行为是未定义的,如果你很幸运,程序会崩溃,但如果你不走运,它似乎会工作一段时间,直到程序稍后出现问题,很难发现问题出在哪里在很久以前执行的代码中。
有些人认为最好malloc()
在初始分配和realloc()
之后使用。这个建议是有道理的,尤其是因为你可能不会使用ptr = realloc(ptr, 0);
来释放内存,即使你可以这样做(所以你并不真正需要malloc()
或者free()
因为realloc()
可以执行所有三个操作)。但是 C90 标准要求realloc(0, new_size)
与 等效地工作malloc(new_size)
,而且我知道没有表现不同的 C 库(但可能有一些;我只使用了几个 C 库,尽管大多数是使用最广泛的库)。
但是,在更一般的情况下,例如以下代码,则代码存在一个微妙的问题(但与初始空指针无关):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char *ptr = NULL;
size_t len = 0;
char buffer[256];
while (fgets(buffer, sizeof(buffer), stdin))
{
size_t buflen = strlen(buffer) + 1;
if (buflen > len)
{
if ((ptr = realloc(ptr, buflen)) == 0) // Danger!
// ... handle memory allocation failure ...
len = buflen;
}
strcpy(ptr, buffer);
// ... do something with ptr
}
free(ptr);
return 0;
}
有什么危险?危险在于,如果第二次或随后的内存分配失败并且ptr
是指向已分配内存的唯一指针,您只需用 null 覆盖其先前的值。这意味着您不能再释放分配的内存ptr
——您已经泄漏了内存。(对于第一次分配,初始值为 0,被覆盖的值为 0,什么都没有改变;没有内存泄漏。这就是为什么在代码中添加了循环。)
经验法则
- 不要写
ptr = realloc(ptr, newsize);
将新值保存到单独的变量中,直到您对其进行测试。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char *ptr = NULL;
size_t len = 0;
char buffer[256];
while (fgets(buffer, sizeof(buffer), stdin))
{
size_t buflen = strlen(buffer) + 1;
if (buflen > len)
{
char *new_ptr = realloc(ptr, buflen);
if (new_ptr == 0)
// ... handle memory allocation failure ...
ptr = new_ptr;
len = buflen;
}
strcpy(ptr, buffer);
// ... do something with ptr
}
free(ptr);
return 0;
}
此代码不会在分配失败时泄漏内存。
辅助建议:不要使用名为new
;的变量。这将使使用 C++ 编译器编译变得困难。即使您现在不打算转换为 C++(即使您最终可能会重写内存管理),使用 C++ 关键字new
作为 C 变量名也没有任何好处……除非您明确想要防止使用 C++ 编译器进行编译。