在 NULL 值(或未定义)指针上重新分配
Posted
技术标签:
【中文标题】在 NULL 值(或未定义)指针上重新分配【英文标题】:Realloc on NULL-valued (or undefined) pointer 【发布时间】:2012-08-21 11:43:34 【问题描述】:我正在阅读有关realloc
的内容,但对其中提到的一点感到困惑。考虑下面的代码:
#include <stdio.h>
#include <stdlib.h>
int main ()
int* ptr = NULL;
ptr = realloc(ptr, 10*sizeof(int));
return 0;
使用最初的NULL
-valued ptr
为realloc
分配内存有什么危险吗?如果不是:
int* ptr = NULL;
我有这个:
int* ptr; // no value given to ptr
使用ptr
调用realloc
会不会有问题?
【问题讨论】:
ptr = realloc(ptr, 10*sizeof(int));
如果 realloc 失败,你正在泄漏内存。
@AyxanHaqverdili 但在这种情况下没有内存泄漏。
@Kaihaku 我可能说的是一般情况。
【参考方案1】:
使用 realloc 分配内存有什么危险吗? 最初为 NULL 值的 ptr
无
7.22.3.5
如果 ptr 是空指针,realloc 函数的行为类似于 malloc 指定大小的函数。
第二部分:
int* ptr; // no value given to ptr
使用 ptr 调用 realloc 会不会有问题?
如果您使用的是未初始化的指针,那么这确实是一个非常严重的问题,因为您无法预测它们的值将是什么。函数realloc
仅适用于NULL
或从malloc
/ realloc
获得的值。
否则,如果 ptr 不匹配之前由 a 返回的指针 内存管理功能 [...] 行为未定义
【讨论】:
请注意,这样做的原因是malloc
的大多数实现在指针返回之前存储块的长度(允许free
知道要释放多少内存)。如果你给realloc
一个未初始化的指针,它会认为它是一个有效的指针(指针就是指针,realloc
所能做的就是相信你)。然后,此实现将尝试将其前面的几个字节 (size_t) 解释为块的大小,这显然是不正确的。这就是为什么你必须显式地为空指针,以便知道它不是一个有效的地址。
@Mk12:你的意思是它在指针返回后立即存储块的长度(或者更准确地说,它存储指针指向的块的长度)?对不起,我很困惑。
@curvature:假设我们有一台非常旧的计算机,它的内存空间只有 256 个字节。指针和size_t
只需要 1 字节宽,因为 1 字节可以容纳 256 个不同的值。如果你调用malloc(13)
,它会找到一些内存。它会返回一个指向内存地址的指针,比如 0x5,但它实际上将数字 13 存储在它之前的 0x4 中。这样,当您在 0x5 上调用 free
时,它会查看它之前的字节 (0x4),发现它包含数字 13,然后它知道它必须释放 13 个字节(所以它不会只释放 0x5,它还将释放 0x6、0x7、0x8 等)
所以如果你realloc
一个未初始化的指针(它可以指向任何东西),它会查看它之前的字节,也许它包含值103,谁知道呢?分配新内存后,它将在那里释放 103 个字节,因为它假设这是您过去分配的内容,而您现在想分配给 realloc
。【参考方案2】:
使用 realloc 使用最初的 NULL 值 ptr 分配内存有什么危险吗?
不,这就像malloc
。
如果不是:
int* ptr = NULL;
我有这个:
int* ptr; // no value given to ptr
使用 ptr 调用 realloc 会不会有问题?
是的,会有问题。如果realloc
没有得到NULL
,它将尝试从该位置开始扩展内存,或者可能尝试到free
和malloc
内存的另一部分。由于未初始化的变量可以有任何值,机会非常高,它们不是realloc
喜欢的值。如果幸运的话,您的程序会立即崩溃。
【讨论】:
【参考方案3】:有了具体代码,一开始使用空指针是没有问题的。
如果变量 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++ 编译器进行编译。
【讨论】:
这是一个非常漂亮的答案。不幸的是,我已经接受了另一个(也很好)的...... 使用ptr = realloc(ptr,newsize
通常很好,因为如果分配失败,程序将无法有效地继续进行,尽管将 realloc 包装在一个将输出诊断信息并调用 exit
的函数中失败的情况可能比必须在各处分散代码以检查返回值并在失败时中止更实际。太糟糕了,没有用于缩小分配而不使任何指向它的指针无效的函数,因为代码可以简单地假设这样的函数总是会成功(即使系统不能......
...实际上无论出于何种原因缩小分配,它可以简单地保持原样并且用户代码不需要关心)。以上是关于在 NULL 值(或未定义)指针上重新分配的主要内容,如果未能解决你的问题,请参考以下文章
SCRIPT5009:“JSON”在 IE 10 中未定义属性“$”的值为 null 或未定义,不是函数对象
TypeScript 编译器崩溃:publicMembers 为 null 或未定义