malloc(0)的意义是什么?

160
我刚刚看到了这个code:
artist = (char *) malloc(0);

...我在想为什么会有人这样做呢?

请问您需要翻译的内容是:https://dev59.com/r3NA5IYBdhLWcg3wI6V4 - shodanex
18个回答

154
根据规范,malloc(0)将返回“空指针或可成功传递给free()的唯一指针”。
基本上这让你可以分配空间,但仍然可以将“artist”变量传递给free()而不必担心。实际上,这与执行以下操作几乎相同:
artist = NULL;

66
我认为将变量设为NULL是更好的跨平台策略,因为规范保证free()在输入NULL时可以正常工作。 - Reed Copsey
3
正如C. Ross所提到的,一些平台在这里可能会返回指针(即“可以传递给free”的唯一指针),但是如果你将其视为char*处理,则可能会得到一个无效的未终止字符。在跨平台情况下依赖此行为可能是危险的。 - Reed Copsey
14
希望说明规格书中也能注明“已安全地传递给realloc”-.- - hanshenrik
1
@NSAddict "空结构体,其sizeof返回0",请提供一个例子,听起来像是一种语言扩展。 - chux - Reinstate Monica
6
谁说你不能这么做?realloc() 允许你传递 malloc() 返回的任何有效指针。就足够了。 - glglgl
显示剩余5条评论

60
C标准(C17 7.22.3/1)规定:
如果请求的空间大小为零,则行为是实现定义: 要么返回null指针,要么行为就像大小为某个非零值一样, 但返回的指针不得用于访问对象。
因此,malloc(0)可能会返回NULL或一个有效的指针,但该指针可能无法被dereference。 在任何情况下,都可以在其上调用free()。
我认为malloc(0)没有什么用处,除非在循环中例如调用malloc(n),其中n可能为零。
从链接中的代码来看,我相信作者有两个误解:
• malloc(0)总是返回一个有效的指针 • free(0)是不好的。
因此,他确保artist和其他变量始终具有某些“有效”值。 注释如下://这些必须始终指向malloc'd数据。

12
这个事实取决于具体实现,使其几乎完全没有用处 - 这是 C 标准中比较糟糕的部分之一,很多标准委员会(例如 P.J. Plauger)都对此表示不满。 - anon
10
我同意。如果malloc(0)返回一个有效指针,那么malloc()返回NULL就意味着"失败",而0也不再是特殊情况,这更加一致。 - Alok Singhal
1
由于malloc在无法获得内存时的情况是实现定义的,因此一种实现方式是将大小为0的分配始终定义为不可满足(ENOMEM)。现在malloc(0)返回0(并且errno == ENOMEM)是一致的。 :-) - R.. GitHub STOP HELPING ICE
7
你可以对malloc(0)返回的指针使用realloc吗?你可以对(char*)NULL使用realloc吗? - Braden Best
4
可以,两个都是。 - chux - Reinstate Monica
显示剩余3条评论

13

malloc(0)的行为取决于实现。库可以返回NULL或具有常规的malloc行为,但不会分配任何内存。无论它做什么,都必须在某个地方进行记录。

通常情况下,它会返回一个有效且唯一的指针,但不应对其进行解引用操作。还要注意,即使它实际上没有分配任何内容,也可能会消耗内存。

可以realloc非空的malloc(0)指针。

然而,直接使用malloc(0)并没有多少用处。它通常用于动态分配大小为零的情况,并且您并不关心是否验证了它。


10
malloc()必须在某个地方保留“管理信息”(例如分配的块的大小和其他辅助数据)。因此,如果malloc(0)不返回NULL,它将使用内存存储该信息,如果没有通过free()释放,就会构成内存泄漏。 - Alok Singhal
1
消耗的内存和分配的内存并不是同一回事。在这种情况下,大多数实现将返回一个唯一的指针。这意味着地址空间的一部分需要为该指针牺牲。根据分配器的不同,这可能实际上意味着它将分配1个或更多字节。 - Coincoin
1
这个库可以随心所欲地做任何事情 - 它可以返回一个没有其他malloc()会返回的唯一指针,或者返回NULL - Alok Singhal
@Steve:我只是好奇而已,谢谢。 - jldupont
1
@jldupont:至少微软C运行时库为'malloc(0)'返回一个唯一的指针。然而,在标准C库的同一实现中,'realloc(ptr, 0)'释放'ptr'并返回NULL。 - Medinoc
显示剩余4条评论

7
在本页的其他答案中有一个回答,开始于“malloc(0)将返回一个有效的内存地址,其范围取决于被分配内存的指针类型”。这个说法是不正确的(我没有足够的声誉在那个答案下直接评论,因此不能直接在那里放置此评论)。
执行malloc(0)不会自动分配正确大小的内存。malloc函数不知道您正在将其结果转换为什么类型。malloc函数纯粹依赖您提供的大小数字作为其参数。例如,您需要执行malloc(sizeof(int))来获得足够的存储空间来容纳int,而不是0。

6
这里有很多半真半假的答案,以下是硬性事实。 malloc() 的手册页面表示:

如果大小为0,则 malloc() 返回 NULL 或者可以随后成功传递给 free() 的唯一指针值。

这意味着,malloc(0) 的结果绝对不能保证是唯一的或非NULL的。唯一的保证来自于 free() 的定义,再次引用手册页面:

如果 ptr 为 NULL,则不执行任何操作。

因此,无论 malloc(0) 返回什么,都可以安全地传递给 free()。但一个 NULL 指针也可以。
因此,编写 artist = malloc(0); 绝不比编写 artist = NULL; 更好。

1
很遗憾,实现不允许返回非空、非唯一的指针。这样,malloc(0)就可以返回0x1,而free()可以像对0x0一样特殊处理0x1。 - Todd Lehman
3
实现可以按照你的建议进行。C规范没有规定结果必须是“NULL或唯一指针”,而是“一个空指针或指向分配空间的指针”。因此,没有“唯一”的要求。但是,返回一个非唯一特殊值可能会破坏依赖于唯一值的代码。也许这是一个适合在Stack Overflow上探讨的角落案例问题。 - chux - Reinstate Monica
1
“man” 可以记录在 *nix 中使用的实现定义形式。但在这种情况下,它并没有成为一般 C 的规范来源。 - Lundin
@Lundin 确实如此。但是与C标准相比,man页面更易于访问,并且GNU / Linux系统上的man页面通常很好地记录了实现遵循的标准。除了哪些部分遵循哪个标准的信息外,如果它们不同的话。我感觉他们都想要精确,并且宣传每一个GNU扩展位... - cmaster - reinstate monica

3

为什么你不应该这样做...

由于malloc的返回值是实现相关的,你可能得到一个空指针或其他地址。如果错误处理代码没有检查大小和返回值,就会出现堆缓冲区溢出,导致稳定性问题(崩溃)或更严重的安全问题。

考虑以下示例,在返回的地址上进一步访问内存,如果大小为零且实现返回一个非NULL值,则会破坏堆。

size_t size;
 
/* Initialize size, possibly by user-controlled input */
 
int *list = (int *)malloc(size);
if (list == NULL) {
  /* Handle allocation error */
}
else {
  /* Continue processing list */
}

请查看CERT编码标准的此安全编码页面,其中我引用了上面的例子以供更进一步阅读。


链接已更改:https://wiki.sei.cmu.edu/confluence/display/c/MEM04-C.+小心零长度分配 - alx - recommends codidact

3

malloc(0)对我来说没有任何意义,除非代码依赖于特定实现的行为。如果代码需要可移植性,那么必须考虑到从malloc(0)返回NULL并不是失败。那么为什么不把NULL分配给artist呢?因为这是一个有效的成功结果,而且代码更少,不会让维护程序员花时间去理解。

malloc(SOME_CONSTANT_THAT_MIGHT_BE_ZERO)malloc(some_variable_which_might_be_zero)可能有它们的用途,尽管您必须特别小心,以免将NULL返回视为失败,如果值为0,则0大小应该是可以接受的。


2
诚然,我以前从未见过这种语法,这是我第一次看到,可以说是一个经典的函数过度使用案例。除了Reed的答案之外,我想指出还有一种类似于重载函数realloc的东西:
  • foo不为NULL且size为零,realloc(foo, size);。当您将非NULL指针和大小为零的值传递给realloc时,realloc的行为就像您调用了free(...)。
  • foo为NULL且size不为零且大于1,realloc(foo, size);。当您传入一个空指针并且大小为非零时,realloc的行为就像您调用了malloc(...)。
希望对您有所帮助, 最好的问候, 汤姆。

1

在Windows系统中:

  • void *p = malloc(0);会在本地堆上分配一个零长度的缓冲区。返回的指针是一个有效的堆指针。
  • malloc最终使用默认的C运行时堆调用HeapAlloc,然后调用RtlAllocateHeap等函数。
  • free(p);使用HeapFree来释放堆上的0长度缓冲区。不释放会导致内存泄漏。

0

编写代码的人可能希望您将malloc(0)视为一个空初始化数组,可以向其中添加值(使用realloc())。虽然这样做无论malloc(0)返回NULL还是实际指针都可以工作, 但它会引起误解:

如果没有实现意识,您将无法确定malloc(0)返回的潜在NULL的含义。它可能意味着您的内存已用完,但也可能意味着实现决定为大小为0的分配返回该值。即使这对您的代码无关紧要,它也可能引起一些疑问。

这意味着malloc(0)是尝试编写更易读的代码的尝试。但是,由于它涉及特定于实现的行为并在此过程中丢失信息,因此很可能会导致混淆。除非您明确需要实现特定行为的结果,否则不要使用它。


1
那么,如果您调用malloc(0),如何可移植地处理检查失败呢?如果您根本不使用malloc(0),则malloc()返回NULL失去了作为错误条件的所有特殊含义。这正好与“高级语言程序员”的情况相反 - 您的代码丢失了信息,现在必须编写额外的代码来弥补这一点。 - Andrew Henle
1
这似乎是个坏主意。像这样使用malloc(0)是不符合惯用法的。我不确定“转移责任”的意义在哪里;使用malloc(0)的结果并没有更好地分配一个正确的值,就像分配NULL一样(更少因为分配的值对malloc(0)是实现定义的),并且当然它把检查分配错误的责任转移到可能稍后调用realloc。我没有看到任何关于这个问题的“更高级别”的东西;它似乎很混乱。 - ad absurdum
1
我已经重写了我的答案以反映您的批评。请注意,我希望这个答案能够脱颖而出,因为它描绘了一条基于开发者特异性的推理线路,而不是代码实际执行的内容。 - Zyl

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