13

如果执行下一个:

int* array = malloc(10 * sizeof(int));

他们我使用realloc:

array = realloc(array, 5 * sizeof(int));

在第二行(并且只有它),它可以返回NULL吗?

4

7 回答 7

13

是的,它可以。上没有实现保证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和分配器)甚至不支持这样的事情。他们总是希望你分配一个新块。但这只是一个松散的评论。

于 2012-08-25T20:20:52.103 回答
6

其他答案已经解决了这个问题,但是假设您知道realloc呼叫是“修剪”,您可以将其包装为:

void *safe_trim(void *p, size_t n) {
    void *p2 = realloc(p, n);
    return p2 ? p2 : p;
}

并且返回值将始终指向 size 的对象n

在任何情况下,由于实现realloc知道对象的大小,因此可以确定它正在“修剪”,从实现质量的角度来看,不在内部执行上述逻辑将是病态的糟糕。但是由于realloc不需要这样做,你应该自己做,或者使用上面的包装器,或者在调用时使用类似的内联逻辑realloc

于 2012-08-25T20:51:19.490 回答
3

语言(和库)规范没有做出这样的保证,就像它不保证“修剪”realloc将保留指针值一样。

一个实现可能决定以最“原始”的方式实现:通过对新内存块realloc执行无条件操作,复制数据并 -处理旧块。显然,这种实现在内存不足的情况下可能会失败。mallocfree

于 2012-08-25T20:19:22.470 回答
3

不要指望它。标准没有这样的规定;它仅声明“如果无法分配新对象,则为空指针”。

你很难找到这样的实现,但根据标准,它仍然是合规的。

于 2012-08-25T20:19:28.160 回答
2

我怀疑在您描述的情况下,理论上可能存在失败的可能性。

根据堆的实现,可能没有修剪现有分配块这样的事情。相反,首先分配一个较小的块,然后从旧块复制数据,然后释放它。

例如,桶堆策略可能就是这种情况(一些流行的堆使用,例如 tcmalloc)。

于 2012-08-25T20:21:51.250 回答
2

有点晚了,但至少有一种流行的实现realloc()可能会失败:TCMalloc。(至少据我了解代码)

如果你读取文件tcmalloc.cc,在函数do_realloc_with_callback()中,你会看到如果你收缩得足够多(分配的内存的50%,否则会被忽略),TCMalloc 会先分配新的内存(可能会失败)然后复制它并删除旧的记忆。

我不复制源代码,因为我不确定(TCMalloc 和 Stackoverflow 的)版权是否允许这样做,但这里是源代码的链接(修订于 2019 年 5 月 17 日)。

于 2017-08-21T15:36:10.080 回答
-1

realloc收缩现有内存不会失败,因此不会返回NULLNULL只有在扩展过程中失败时才能返回。

但是在某些架构中收缩可能会失败,realloc可以以不同的方式实现,例如单独分配较小的内存并释放旧内存以避免碎片。在这种情况下,缩小内存可以返回 NULL。但它的实现非常罕见。

但最好是在更安全的一面,NULL在缩小内存后进行检查。

于 2012-08-25T20:16:28.963 回答