修剪时realloc会失败(返回NULL)吗?

时间:2012-08-25 20:14:25

标签: c memory-management realloc

如果做下一个:

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

他们使用realloc:

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

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

7 个答案:

答案 0 :(得分:11)

是的,它可以。 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&lt; = 6,它分配32个字节,而对于7-10,它是48个。

因此,如果它将int[10]缩减为int[5],则分配的大小将从48缩减到32,实际上有16个空闲字节。因为(正如它已经注意到的)它不会分配少于32个字节的东西,这16个字节就会丢失。

如果它将块移动到其他位置,则将释放整个48个字节,并且实际上可以放入其中的某些内容。当然,这只是一个科幻故事,而不是一个真正的实现;)。


C99标准中最相关的引用( 7.20.3.4 realloc函数):

  

<强>返回

     

4 realloc函数返回指向新对象的指针(可能与指向旧对象的指针具有相同的值),如果新对象可以,则返回空指针不被分配。

'May'是这里的关键词。它没有提到可能发生的任何特定情况,所以你不能依赖它们中的任何一个,即使它们乍一看听起来很明显。


顺便说一下,我认为你可以认为realloc()有点弃用了。如果您看一下C ++,那么较新的内存分配接口(new / delete和分配器)甚至不支持这样的事情。他们总是希望你分配一个新的块。但这只是一个松散的评论。

答案 1 :(得分:5)

其他答案已经解决了问题,但假设您知道realloc调用是“修剪”,您可以用以下内容进行包装:

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

并且返回值将始终指向大小为n的对象。

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

答案 2 :(得分:3)

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

实现可能决定以最“原始”的方式实现realloc:通过为新内存块执行无条件malloc,复制数据和free - 旧的块。显然,这种实现可能会在内存不足的情况下失败。

答案 3 :(得分:3)

不要指望它。该标准没有规定;如果无法分配新对象,它只会声明“或空指针”。

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

答案 4 :(得分:2)

我怀疑在您描述的情景中可能存在理论失败的可能性。

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

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

答案 5 :(得分:1)

有点晚了,但至少有一个流行的实现,realloc()具有超大尺寸可能会失败:TCMalloc。 (至少就我理解的代码而言)

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

我不复制源代码,因为我不确定(TCMalloc和Stackoverflow)的版权是否允许,但这里是link to the source(修订截至2019年5月17日)。

答案 6 :(得分:-1)

realloc在缩小现有内存时不会失败,因此不会返回NULL。只有在扩展期间失败时,它才能返回NULL

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

但更好的做法是在更安全的一面,在缩小内存后保持NULL检查。