为什么malloc(0)的返回值是实现定义的?

18

ISO/IEC 9899:TC2(即C99标准)第7.20.3节规定:

如果请求空间的大小为零,则行为是实现定义的:要么返回空指针,要么行为就像大小为非零值一样,除了不能使用返回的指针来访问对象。

换句话说,malloc(0)可能会返回NULL,也可能会返回一个有效的指针,但不能对其进行引用。

这种行为背后的原理是什么?
难道直接定义malloc(0)会导致未定义行为不更容易吗?


16
我从未想过有人会争论C99应该具有更多未定义行为。 - Pascal Cuoq
1
@yes123:我不知道你为什么要这样做,但是你可能会调用malloc(n)而没有检查n是否等于0。 - dan04
请阅读这里的已接受答案:https://dev59.com/QXI95IYBdhLWcg3w1Bro - Prasoon Saurav
3个回答

16

C99 Rationale(PDF链接)讨论了内存管理函数(来自C99 7.20.3)并解释说:

在这些函数的定义中,对空指针和零长度分配请求的处理部分受到希望支持此范例的影响:

OBJ * p; // pointer to a variable list of OBJs
    /* initial allocation */
p = (OBJ *) calloc(0, sizeof(OBJ)); 
    /* ... */
    /* reallocations until size settles */
while(1) { 
    p = (OBJ *) realloc((void *)p, c * sizeof(OBJ)); 
      /* change value of c or break out of loop */
} 

报告称,这种编码风格虽然不一定得到委员会的认可,但却被广泛使用。

有些实现在分配零字节的请求时会返回非空值。
尽管这种策略具有区分“无”和“零”的理论优势(未分配指针与指向零长度空间的指针之间的区别),但它具有更为强制性的理论缺点,需要引入零长度对象的概念。

由于这样的对象不能被声明,它们唯一存在的方式是通过这样的分配请求。

C89委员会决定不接受零长度对象的想法。因此,分配函数可能会对零字节的分配请求返回一个空指针。请注意,这种处理方式并不排除上述范例。

C89中的悄悄变化:依赖于大小为零的分配请求返回非空指针的程序将表现出不同的行为。


2
如果我没记错的话,如果请求分配零字节总是导致空指针,那么上述范例仍然完美地工作,因为realloc(NULL, n) == malloc(n)。对我来说,上述范例并没有解释为什么需要有malloc(0)的两个可能的返回值。 - Philip
然而,我认为这将是我们能得到的最好答案,所以我接受它。 - Philip
@Philip:开头的例子是解释为什么结果不是未定义的。它可以工作,无论是返回null还是其他指针,但如果结果未定义,则无法工作。下面的解释我认为是与示例分开的,因此允许返回null或其他指针的原因是“一些实现已经返回了零字节分配请求的非空值”,但是“C89委员会决定……分配函数因此可能会对零字节的分配请求返回null指针。” - James McNellis
实际上,@R.的答案解释得很好:在有许多实现在某些情况下具有不同行为之前,C语言并没有标准化,因此C90是一种“妥协”。 - James McNellis

9
因为分配0字节有时候是有意义的。例如,当您分配一个未知数量的项目的数组时。UB会导致程序崩溃,而当前的行为可以安全地分配numberOfItems * itemSize字节。
逻辑如下:如果您请求0字节,则会返回指针。当然,您不能对其进行解引用,因为这将访问第0个字节(您尚未分配)。但您可以安全地释放内存。因此,您不需要将0作为特殊情况。
这是关于为什么不将malloc(0)定义为UB的原因。关于决定不严格定义结果(NULL vs.唯一指向空间的指针),请参见James的答案。(简而言之:两种方法都有优缺点。返回唯一的非空指针的想法更具说服力,但需要更多的概念工作,并对实现者施加更大的负担。)

3
如果malloc(0)返回NULL,那么我也可以安全地释放指针。 - Philip
是的,使用返回的任何指针都是允许的:NULL或非NULL。 - Vlad
如果realloc调整大小为零并返回指向最小分配的指针,则在随后使用相同指针进行非零大小请求时能够重用相同空间(如果仍然可用),这种情况有时可能是有利的;如果realloc可以为大小为零的请求返回非空值,则允许malloc也可以这样做是有意义的。 - supercat

5
使malloc(0)的结果为未定义行为会更糟。目前,只要您在调用free时保持一致,就不必关心大小为零时会发生什么。
问题在于,一些现有实现为malloc(0)分配指针,一些返回空指针,几乎所有这些实现都坚决坚持自己的行为,因为编写这些实现的人编写了大量糟糕的、不可移植的软件,这些软件使用了他们选择的行为(GNU在这方面是最严重的罪犯之一)。因此,标准被卡住了,允许两种行为以使所有人都满意。

没有参考文献?!我同意需要“free”的对称性,我认为这使它变得“纯粹”。以下是一些相关链接。 - Matt Joiner
抱歉,我目前不知道任何相关链接。但是从我的经验来看,GNU软件(通过autoconf/gnulib)坚持测试系统是否返回非空指针的malloc(0),如果没有,则会进行可怕的包装处理。 - R.. GitHub STOP HELPING ICE

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接