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

Nov*_*vak 7 c memory-management realloc

如果做下一个:

int* array = malloc(10 * sizeof(int));
Run Code Online (Sandbox Code Playgroud)

他们使用realloc:

array = realloc(array, 5 * sizeof(int));
Run Code Online (Sandbox Code Playgroud)

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

Mic*_*rny 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]);                                    
    }                                                                        
}
Run Code Online (Sandbox Code Playgroud)

并且对于n <= 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和分配器)甚至不支持这样的东西.他们总是希望你分配一个新的块.但这只是一个松散的评论.

  • 通常,我发现用C++代码和C++关于弃用的想法回答C问题有点奇怪. (8认同)
  • 我必须反对仅仅因为C ++在new / delete机制中没有类似物而反对调用`realloc`。C ++与C语言是一种非常不同的语言,特别是,对C ++中移动对象的支持将需要某种实现方式来通知对象它已被重定位并允许其更新自己的内部引用。另一方面,C不会自动执行或封装任何此类操作,因此由调用方(因此完全可以)负责在realloc之后是否需要更改对象的内容。 (3认同)

R..*_*R.. 5

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

void *safe_trim(void *p, size_t n) {
    void *p2 = realloc(p, n);
    return p2 ? p2 : p;
}
Run Code Online (Sandbox Code Playgroud)

返回值将始终指向一个size的对象n

无论如何,由于的实现realloc知道对象的大小,因此可以确定它是“修剪”的对象,因此从实现质量的角度来看,如果不在内部执行上述逻辑,这在病理上是不好的。但是由于realloc不需要这样做,因此您应该自己进行操作,或者使用上述包装程序或在调用时使用类似的内联逻辑realloc

  • 是的,我相信是的。修剪存储以获取现有结果的代码可能无法“撤消”失败的进度,并无法以有意义的方式向更高级别的代码报告失败。因此,以“不会失败”的方式编写代码非常有价值。即使下一次对`malloc`的调用将在其他地方失败,这(至少在一个健壮的程序中)将处于该程序可以处理失败情况,退出任何部分工作并报告错误的地步。 。 (2认同)
  • 是的,当然是。如果不是,`realloc`在健壮的程序中将毫无用处。这实际上是内存泄漏的一种非常常见的形式(即p = realloc(p,newsize);如果realloc失败,它将丢失旧的内存)。 (2认同)