【问题标题】:What's the point of malloc(0)?malloc(0) 的意义何在?
【发布时间】:2011-01-02 14:25:16
【问题描述】:

我刚刚看到这个code:

artist = (char *) malloc(0);

...我想知道为什么要这样做?

【问题讨论】:

标签: c malloc


【解决方案1】:

根据规范,malloc(0) 将返回“可以成功传递给 free() 的空指针或唯一指针”。

这基本上让您什么都不分配,但仍然可以将“艺术家”变量传递给对 free() 的调用,而无需担心。出于实际目的,它与做的几乎相同:

artist = NULL;

【讨论】:

  • 就我个人而言,我认为设置为 NULL 是一种更好的跨平台策略,因为 free() 保证(按规范)在 NULL 作为输入时可以正常工作。
  • 正如 C. Ross 所提到的,从技术上讲,某些平台可以在此处返回一个指针(即“可以传递给 free 的唯一指针”),但如果您将其视为char*,这可能会给你一个无效的、未终止的字符。在跨平台情况下依赖它可能很危险。
  • 真的希望规范也会说“安全地传递给 realloc”-.-
  • @NSAddict "sizeof 将返回 0 的空结构",请提供示例,听起来像是语言扩展。
  • @hanshenrik 谁说你不能? realloc() 允许您传递malloc() 返回的任何有效指针。应该够了。
【解决方案2】:

C 标准 (C17 7.22.3/1) 说:

如果请求的空间大小为零,则行为由实现定义: 要么返回一个空指针,要么行为就像大小是一些 非零值,但返回的指针不得用于访问对象。

因此,malloc(0) 可以返回 NULL 或一个有效的指针可能不会被取消引用。无论哪种情况,调用free() 都是完全有效的。

我真的不认为malloc(0) 有什么用处,除非在循环中调用malloc(n) 时,n 可能为零。

看链接里的代码,相信作者有两个误解:

  • malloc(0) 返回一个有效的指针总是,并且
  • free(0) 不好。

因此,他确保artist 和其他变量始终具有一些“有效”值。评论也说了这么多:// these must always point at malloc'd data

【讨论】:

  • 它依赖于实现的事实使其或多或少完全无用 - 这是 C 标准中更糟糕的部分之一,并且相当多的标准委员会(例如 PJ Plauger)有抱怨它。
  • 我同意。如果malloc(0)返回了一个有效的指针,那么malloc()返回NULL总是意味着“失败”,0不再是特例,更加一致。
  • 由于malloc获取内存失败的情况是实现定义的,实现可以简单地定义size-0分配总是不可满足的(ENOMEM),现在malloc(0)返回0 (与errno==ENOMEM)是一致的。 :-)
  • 你能realloc 一个malloc(0) 返回的指针吗?可以realloc((char*)NULL)吗?
  • @Braden 两者都同意。
【解决方案3】:

malloc(0) 行为是特定于实现的。该库可以返回 NULL 或具有常规 malloc 行为,而无需分配内存。无论它做什么,都必须记录在某个地方。

通常,它返回一个有效且唯一的指针,但不应取消引用。另请注意,即使它实际上没有分配任何东西,它也会消耗内存。

可以重新分配非空 malloc(0) 指针。

尽管逐字逐句地使用 malloc(0) 并没有多大用处。它主要用于动态分配为零字节并且您不关心对其进行验证的情况。

【讨论】:

  • malloc() 必须在某处保留“内务管理信息”(例如,分配的块的大小和其他辅助数据)。所以,如果malloc(0)不返回NULL,它会使用内存来存储那个信息,如果不是free()d,就会构成内存泄漏。
  • Malloc 实现执行记录保存,这可能会在请求的大小之上为每个返回的指针添加一定数量的数据。
  • 消耗的内存和分配的内存不是一回事。在这种情况下,大多数实现将返回一个唯一指针。这意味着需要为该指针牺牲一部分地址空间。根据分配器的不同,这实际上可能意味着它将分配 1 个字节或更多。
  • 库可以为所欲为 - 好吧,它可以返回一个唯一的指针,其他malloc() 不会返回,或者返回NULL
  • @jldupont:至少 Microsoft C 运行时库为 malloc(0) 返回一个唯一指针。然而,在标准 C 库的相同实现中,realloc(ptr, 0) 释放 ptr 并返回 NULL。
【解决方案4】:

在此页面的其他地方有一个答案,开头是“malloc(0) 将返回一个有效的内存地址,其范围将取决于分配内存的指针类型”。这个说法是不正确的(我没有足够的声望直接评论那个答案,所以不能把这个评论直接放在下面)。

执行 malloc(0) 将不会自动分配正确大小的内存。 malloc 函数不知道您将其结果转换为什么。 malloc 函数完全依赖于您作为参数提供的大小数。您需要执行 malloc(sizeof(int)) 以获得足够的存储空间来保存 int,例如,不是 0。

【讨论】:

    【解决方案5】:

    这里有很多半真半假的答案,所以这里是确凿的事实。 malloc() 的手册页说:

    如果 size 为 0,则 malloc() 返回 NULL 或稍后可以 成功传递给free()。

    这意味着,绝对不能保证malloc(0) 的结果是唯一的或者不是NULL。唯一的保证是由free() 的定义提供的,同样,这是手册页所说的:

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

    所以,无论malloc(0) 返回什么,都可以安全地传递给free()。但是NULL 指针也可以。

    因此,写作 artist = malloc(0); 绝不比写作好 artist = NULL;

    【讨论】:

    • 太糟糕了,不允许实现返回非空、非唯一的指针。这样,malloc(0) 可以返回 0x1,free() 可以像对 0x0 一样对 0x1 进行特殊情况检查。
    • @Todd Lehman 实现可以按照您的建议进行。 C 规范没有指定结果必须是“NULL,或唯一指针”。而是“空指针或指向分配空间的指针”。没有 unique 要求。OTOH,返回非唯一的特殊值可能会破坏依赖唯一值的代码。也许是极端情况SO的问题。
    • man 也可以记录 *nix 中使用的实现定义的形式。在这种情况下它没有,但它仍然不是通用 C 的规范来源。
    • @Lundin True。但是手册页比 C 标准更容易访问,GNU/Linux 系统上的手册页通常很好地记录了实现遵循的标准。以及哪些部件遵循哪个标准的信息,它们是否应该有所不同。我有一种感觉,他们都想要精确,并宣传每一个 GNU 扩展......
    【解决方案6】:

    malloc(0) 对我来说没有任何意义,除非代码依赖于特定于实现的行为。如果代码是可移植的,那么它必须考虑到从malloc(0) 返回的 NULL 不是失败的事实。那么为什么不直接将 NULL 分配给 artist,因为这是一个有效的成功结果,并且代码更少,并且不会导致您的维护程序员花时间来解决这个问题?

    malloc(SOME_CONSTANT_THAT_MIGHT_BE_ZERO)malloc(some_variable_which_might_be_zero) 可能有它们的用途,尽管如果值为 0,您必须再次注意不要将 NULL 返回视为失败,但 0 大小应该是可以的。

    【讨论】:

      【解决方案7】:

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

      由于 malloc 的返回值依赖于实现,您可能会得到一个 NULL 指针或其他地址。如果错误处理代码不检查大小和返回值,这最终会导致堆缓冲区溢出,从而导致稳定性问题(崩溃)甚至更严重的安全问题。

      考虑这个例子,如果通过返回的地址进一步访问内存将损坏堆,如果大小为零并且实现返回一个非 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 编码标准中的 this Secure Coding page,我在其中举了上面的示例以供进一步阅读。

      【讨论】:

      【解决方案8】:

      诚然,我以前从未见过这种语法,这是我第一次看到这种语法,可以说是函数过度杀伤的经典案例。结合里德的回答,我想指出有一个类似的东西,看起来像一个重载函数realloc

      • foo 为非 NULL 且大小为零,realloc(foo, size);。当你将一个非 NULL 指针和零大小传递给 realloc 时,realloc 的行为就像你调用了 free(...)
      • foo 为 NULL,大小为非零且大于 1,realloc(foo, size);。当你传入一个 NULL 指针并且大小不为零时,realloc 的行为就像你调用了 malloc(...)

      希望这会有所帮助, 最好的祝福, 汤姆。

      【讨论】:

        【解决方案9】:

        要实际回答所提出的问题:没有理由这样做

        【讨论】:

          【解决方案10】:

          在 Windows 中:

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

          【讨论】:

            【解决方案11】:

            它实际上非常有用,而且(显然,恕我直言),返回 NULL 指针的允许行为被破坏了。动态指针不仅对它指向的东西有用,而且它的地址是唯一的。返回 NULL 会删除第二个属性。我编写的所有嵌入式 malloc(事实上经常发生)都有这种行为。

            【讨论】:

              【解决方案12】:

              不确定,根据我发现的一些random malloc source 代码,输入 0 会导致返回值为 NULL。因此,将艺术家指针设置为 NULL 是一种疯狂的方式。

              http://www.raspberryginger.com/jbailey/minix/html/lib_2ansi_2malloc_8c-source.html

              【讨论】:

                【解决方案13】:

                malloc(0) 将返回 NULL 或可以正确传递给 free 的有效指针。尽管看起来它指向的内存是无用的,或者它不能被写入或读取,但这并不总是正确的。 :)

                int *i = malloc(0);
                *i = 100;
                printf("%d", *i);
                

                我们预计这里会出现分段错误,但令人惊讶的是,这会打印 100!这是因为当我们第一次调用 malloc 时 malloc 实际上会要求大量内存。此后对 malloc 的每次调用都会使用该大块中的内存。只有在那个大块结束后,才会请求新的内存。

                使用 malloc(0):如果您处于希望后续 malloc 调用更快的情况,调用 malloc(0) 应该为您完成(边缘情况除外)。

                【讨论】:

                • 写入*i 在您的情况下可能不会崩溃,但它仍然是未定义的行为。当心鼻恶魔!
                • 是的。那是真实的。它是特定于实现的。我已经在 MaxOS X 和一些 Linux 发行版上验证了它。我没有在其他平台上尝试过。话虽如此,我所描述的概念已经在 Brain Kernighan 和 Dennis Ritchie 的《C 编程语言》一书中进行了描述。
                • 我知道:对这个问题的评论太晚了。但是有时没有提到malloc(0) 的用途。在返回非 NULL 值的那些实现中,尤其是在 DEBUG 构建中,它可能分配的比您要求的更多,并为您提供刚刚超过其内部标头的指针。如果您在一系列分配之前和之后获得了实际内存使用情况,这可以让您感觉。例如:void* before = malloc(0); ... void* after = malloc(0); long long total = after - before; 或类似的。
                • 我读过 Brain Kernighan 和 Dennis Ritchie 的“C 编程语言”,但我不记得里面提到过 malloc(0)。你能说一下你指的是哪一章吗?提供准确的报价也很好。
                【解决方案14】:

                这是使用valgrind内存检查工具运行后的分析。

                ==16740== Command: ./malloc0
                ==16740==
                p1 = 0x5204040
                ==16740==
                ==16740== HEAP SUMMARY:
                ==16740==     in use at exit: 0 bytes in 0 blocks
                ==16740==   total heap usage: 2 allocs, 2 frees, 1,024 bytes allocated
                ==16740==
                ==16740== All heap blocks were freed -- no leaks are possible
                

                这是我的示例代码:

                #include <stdio.h>
                #include <stdlib.h>
                #include <unistd.h>
                
                int main()
                {
                   //int i;
                   char *p1;
                
                   p1 = (char *)malloc(0);
                   printf("p1 = %p\n", p1);
                
                   free(p1);
                
                   return 0;
                
                }
                

                默认分配 1024 字节。如果我增加 malloc 的大小,分配的字节数会增加 1025 以此类推。

                【讨论】:

                • 它的实现依赖
                【解决方案15】:

                根据Reed Copsey的回答和 malloc 的手册页,我写了一些例子来测试。我发现 malloc(0) 总是会给它一个独特的价值。 看我的例子:

                char *ptr;
                if( (ptr = (char *) malloc(0)) == NULL )
                    puts("Got a null pointer");
                else
                    puts("Got a valid pointer");
                

                输出将是“Got a valid pointer”,这意味着ptr 不为空。

                【讨论】:

                • 仅仅因为你总是得到一个有效的指针并不意味着它是有保证的。
                【解决方案16】:

                只是为了纠正这里的错误印象:

                artist = (char *) malloc(0); 永远不会返回NULL;它与artist = NULL; 不同。编写一个简单的程序并将artistNULL 进行比较。 if (artist == NULL) 为假,if (artist) 为真。

                【讨论】:

                  【解决方案17】:

                  malloc(0) 将返回一个有效的内存地址,其范围将取决于分配内存的指针类型。您也可以为内存区域分配值,但这应该在使用的指针类型的范围内。您还可以释放分配的内存。我会用一个例子来解释:

                  int *p=NULL;
                  p=(int *)malloc(0);
                  free(p);
                  

                  上面的代码可以在 Linux 机器上的 gcc 编译器中正常工作。如果您有 32 位编译器,那么您可以提供整数范围内的值,即 -2147483648 到 2147483647。同样适用于字符。请注意,如果声明的指针类型发生变化,那么无论malloc 类型转换如何,值的范围都会发生变化,即

                  unsigned char *p=NULL;
                  p =(char *)malloc(0);
                  free(p);
                  

                  p 将采用 char 的 0 到 255 之间的值,因为它被声明为 unsigned int。

                  【讨论】:

                  • Krellan 正确地指出这个答案是错误的:malloc() 对演员一无所知(这实际上完全是 C 语言流利的)。取消引用 malloc(0) 的返回值将调用未定义的行为。
                  猜你喜欢
                  • 2015-03-12
                  • 1970-01-01
                  • 1970-01-01
                  • 2015-06-17
                  • 2012-02-07
                  • 2015-06-27
                  • 2011-07-19
                  相关资源
                  最近更新 更多