如果执行下一个:
int* array = malloc(10 * sizeof(int));
他们我使用realloc:
array = realloc(array, 5 * sizeof(int));
在第二行(并且只有它),它可以返回NULL
吗?
如果执行下一个:
int* array = malloc(10 * sizeof(int));
他们我使用realloc:
array = realloc(array, 5 * sizeof(int));
在第二行(并且只有它),它可以返回NULL
吗?
是的,它可以。上没有实现保证realloc()
,即使在收缩时它也可以返回不同的指针。
例如,如果一个特定的实现对不同的对象大小使用不同的池,realloc()
实际上可能会在池中为较小的对象分配一个新块,并为较大的对象释放池中的块。因此,如果较小对象的池已满,它将失败并返回NULL
。
或者它可能只是决定移动块更好
我只是使用以下程序来获取 glibc 实际分配的内存大小:
#include <stdlib.h>
#include <stdio.h>
int main()
{
int n;
for (n = 0; n <= 10; ++n)
{
void* array = malloc(n * sizeof(int));
size_t* a2 = (size_t*) array;
printf("%d -> %zu\n", n, a2[-1]);
}
}
对于 n <= 6,它分配 32 个字节,对于 7-10,它是 48。
因此,如果它缩小int[10]
到int[5]
,分配的大小将从 48 缩小到 32,有效地提供 16 个空闲字节。由于(正如刚刚提到的)它不会分配小于 32 字节的任何内容,因此这 16 个字节将丢失。
如果它将块移动到其他地方,整个 48 个字节将被释放,并且实际上可以在其中放入一些东西。当然,这只是一个科幻故事,而不是真正的实现;)。
C99 标准中最相关的引用(7.20.3.4realloc
函数):
退货
4 该
realloc
函数返回一个指向新对象的指针(它可能与指向旧对象的指针具有相同的值),如果无法分配新对象,则返回一个空指针。
“五月”是这里的关键词。它没有提到可能发生这种情况的任何具体情况,所以你不能依赖其中任何一个,即使它们乍一看听起来很明显。
顺便说一句,我认为您可以考虑realloc()
有些弃用。如果您看一下 C++,较新的内存分配接口(new
/delete
和分配器)甚至不支持这样的事情。他们总是希望你分配一个新块。但这只是一个松散的评论。
其他答案已经解决了这个问题,但是假设您知道realloc
呼叫是“修剪”,您可以将其包装为:
void *safe_trim(void *p, size_t n) {
void *p2 = realloc(p, n);
return p2 ? p2 : p;
}
并且返回值将始终指向 size 的对象n
。
在任何情况下,由于实现realloc
知道对象的大小,因此可以确定它正在“修剪”,从实现质量的角度来看,不在内部执行上述逻辑将是病态的糟糕。但是由于realloc
不需要这样做,你应该自己做,或者使用上面的包装器,或者在调用时使用类似的内联逻辑realloc
。
语言(和库)规范没有做出这样的保证,就像它不保证“修剪”realloc
将保留指针值一样。
一个实现可能决定以最“原始”的方式实现:通过对新内存块realloc
执行无条件操作,复制数据并 -处理旧块。显然,这种实现在内存不足的情况下可能会失败。malloc
free
不要指望它。标准没有这样的规定;它仅声明“如果无法分配新对象,则为空指针”。
你很难找到这样的实现,但根据标准,它仍然是合规的。
我怀疑在您描述的情况下,理论上可能存在失败的可能性。
根据堆的实现,可能没有修剪现有分配块这样的事情。相反,首先分配一个较小的块,然后从旧块复制数据,然后释放它。
例如,桶堆策略可能就是这种情况(一些流行的堆使用,例如 tcmalloc)。
有点晚了,但至少有一种流行的实现realloc()
可能会失败:TCMalloc。(至少据我了解代码)
如果你读取文件tcmalloc.cc
,在函数do_realloc_with_callback()
中,你会看到如果你收缩得足够多(分配的内存的50%,否则会被忽略),TCMalloc 会先分配新的内存(可能会失败)然后复制它并删除旧的记忆。
我不复制源代码,因为我不确定(TCMalloc 和 Stackoverflow 的)版权是否允许这样做,但这里是源代码的链接(修订于 2019 年 5 月 17 日)。
realloc
收缩现有内存不会失败,因此不会返回NULL
。NULL
只有在扩展过程中失败时才能返回。
但是在某些架构中收缩可能会失败,realloc
可以以不同的方式实现,例如单独分配较小的内存并释放旧内存以避免碎片。在这种情况下,缩小内存可以返回 NULL。但它的实现非常罕见。
但最好是在更安全的一面,NULL
在缩小内存后进行检查。