【问题标题】:What does malloc(0) return? [duplicate]malloc(0) 返回什么? [复制]
【发布时间】:2011-01-09 02:10:00
【问题描述】:

malloc(0) 返回什么?

realloc(malloc(0),0) 的答案是否相同?

#include<stdio.h>
#include<malloc.h>
int main()
{
        printf("%p\n", malloc(0));
        printf("%p\n", realloc(malloc(0), 0));
        return 0;
}

Linux GCC 的输出:

manav@manav-workstation:~$ gcc -Wall mal.c
manav@manav-workstation:~$ ./a.out
0x9363008
(nil)
manav@manav-workstation:~$

malloc(0) 的输出每次都在变化。这是标准答案吗?除了学术研究之外,为什么有人会对获得这样的指针感兴趣?

编辑:

如果malloc(0) 返回虚拟指针,那么下面是如何工作的:

int main()
{
    void *ptr = malloc(0);
    printf("%p\n", realloc(ptr, 1024));
    return 0;
}

编辑:

以下代码为每次迭代输出“可能”。为什么它不会失败?

#include<stdio.h>
#include<malloc.h>
int main()
{

        int i;
        void *ptr;
        printf("Testing using BRUTE FORCE\n");
        for (i=0; i<65000; i++)
        {
                ptr = malloc(0);
                if (ptr == realloc(ptr, 1024))
                        printf("Iteration %d: possible\n", i);
                else
                {
                        printf("Failed for iteration %d\n", i);
                        break;
                }
        }
        return 0;
}

【问题讨论】:

  • @tommieb75:回答原始问题会有所帮助。话虽如此,这个问题有两个部分,第二部分更有趣,而不是欺骗。有关详细信息,请参阅我对此问题的回答。

标签: c linux pointers malloc realloc


【解决方案1】:

其他人已经回答了malloc(0) 的工作原理。我将回答您提出的尚未回答的问题之一(我认为)。问题是关于realloc(malloc(0), 0)

malloc(0) 返回什么? realloc(malloc(0),0) 的答案是否相同?

标准是这样说realloc(ptr, size)的:

  • 如果ptrNULL,它的行为类似于malloc(size)
  • 否则(ptr 不是NULL),它会释放ptr 指向的旧对象指针,并返回一个指向新分配缓冲区的指针。但是如果size 为0,C89 说效果相当于free(ptr)。有趣的是,我在 C99 草案(n1256 或 n1336)中找不到该声明。在 C89 中,在这种情况下返回的唯一合理值是 NULL

所以,有两种情况:

  • malloc(0) 在实现上返回 NULL。那么你的realloc() 调用就相当于realloc(NULL, 0)。这相当于上面的malloc(0)(在本例中为NULL)。
  • malloc(0) 返回非NULL。那么,调用就相当于free(malloc(0))。在这种情况下,malloc(0)realloc(malloc(0), 0)等效。

注意这里有一个有趣的情况:在第二种情况下,当malloc(0)成功返回非NULL时,它仍然可能返回NULL来表示失败。这将导致类似:realloc(NULL, 0) 的调用,这将等同于 malloc(0),它可能会或可能不会返回 NULL

我不确定 C99 中的省略是否是疏忽,或者这是否意味着在 C99 中,非NULLrealloc(ptr, 0) ptr 不等于free(ptr)。我刚刚用gcc -std=c99试了一下,上面的等价于free(ptr)

编辑:我想我明白你的困惑是什么:

让我们看一下示例代码中的一个 sn-p:

ptr = malloc(0);
if (ptr == realloc(ptr, 1024))

以上与malloc(0) == realloc(malloc(0), 1024)不一样。在第二个中,malloc() 调用两次,而在第一个中,您将先前分配的指针传递给 realloc()

让我们先分析第一个代码。假设 malloc(0) 在成功时不返回 NULL,则 ptr 具有有效值。当您执行realloc(ptr, 1024) 时,realloc() 基本上会为您提供一个大小为 1024 的新缓冲区,而 ptr 将变为无效。符合要求的实现可能会返回与 ptr 中已有的地址相同的地址。因此,您的 if 条件可能会返回 true。 (但是请注意,在realloc(ptr, 1024) 之后查看ptr 的值可能是未定义的行为。)

现在你问的问题是:malloc(0) == realloc(malloc(0), 1024)。在这种情况下,我们假设 LHS 和 RHS 上的 malloc(0) 都返回非NULL。然后,保证它们是不同的。此外,LHS 上malloc() 的返回值还不是free()d,因此任何其他malloc()calloc()realloc() 可能不会返回该值。这意味着如果您将条件写为:

if (malloc(0) == realloc(malloc(0), 1024)
    puts("possible");

您不会在输出中看到possible(除非malloc()realloc() 都失败并返回NULL)。

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    void *p1;
    void *p2;

    p1 = malloc(0);
    p2 = realloc(p1, 1024);
    if (p1 == p2)
        puts("possible, OK");

    /* Ignore the memory leaks */
    if (malloc(0) == realloc(malloc(0), 1024))
        puts("shouldn't happen, something is wrong");
    return 0;
}

在 OS X 上,我的代码在运行时没有输出任何内容。在 Linux 上,它打印 possible, OK

【讨论】:

  • @Alok:干得好。我检查了 Linux 上的条件 if (malloc(0) == realloc(malloc(0), 1024)。每次都失败!
  • 当你说“失败”时,你的意思是上面的程序打印“不应该发生,有问题”?
  • "标准对 realloc(ptr, size) 的描述是这样的:如果 ptr 为 NULL,它的行为类似于 malloc(size),否则(ptr 不为 NULL),它释放旧对象指针ptr 并返回一个指向新分配缓冲区的指针。”我觉得我应该提到realloc 仅在必须移动内存区域时执行free(ptr),如果 id 没有,则不执行释放,只是扩展。至少这是我从man-page 所理解的。 “如果指向的区域被移动,则执行 free(ptr)。”
  • 在 C99 和 C11 中,J.3 实现定义的行为 部分包含: -calloc、malloc 和 realloc 函数是否返回空指针或当请求的大小为零 (7.22.3) 时,指向已分配对象的指针。 这意味着 realloc(ptr,0) 允许返回有效(但不可取消引用)指针,如 malloc(0) .
【解决方案2】:

malloc(0) 就 C99 而言是实现定义

来自 C99 [第 7.20.3 节]

连续调用calloc分配的存储顺序和连续性, malloc 和 realloc 函数未指定。如果分配返回的指针 成功被适当地对齐,以便它可以分配给指向任何类型对象的指针 然后用于访问分配的空间中的此类对象或此类对象的数组 (直到空间被显式释放)。分配对象的生命周期延长 从分配到解除分配。每个这样的分配都应产生一个指向 与任何其他对象脱节的对象。返回的指针指向开始(最低字节 地址)的分配空间。如果无法分配空间,则为空指针 回来。 如果请求的空间大小为零,则行为是实现- 定义:要么返回一个空指针,要么行为就像大小是一些 非零值,但返回的指针不得用于访问对象。

【讨论】:

  • PJ Plauger(C 标准委员会成员)在他的一篇文章中说他们对此争论不休,最后他们退缩了,把它留给了实现。
  • @Manav:它可能被用作标记指针:它是一个绝对不同于任何其他有效指针且不为 NULL 的指针。
  • 在 malloc(0) 的情况下返回非空指针是一致的,并有助于将其与返回 NULL 的错误情况区分开来,后者可以从 malloc 的任何参数返回,例如malloc(4).
  • @BlueRaja:这是他们的工作,争论各种提案的利弊。在这种情况下,总是返回空指针对某些人来说很方便,而进行 0 大小的分配对其他人来说很方便。委员会应该考虑两者。不幸的是,他们提出的折衷方案仅有助于特定于平台的代码,对于任何尝试可移植代码的人来说都是无用的。
  • @TMN:因为“每个这样的分配都应该产生一个指向与任何其他对象不相交的对象的指针。”
【解决方案3】:

在 C89 中,malloc(0) 取决于实现 - 我不知道 C99 是否已修复此问题。在 C++ 中,使用:

char * p = new char[0];

定义明确——你得到一个有效的非空指针。当然,您不能在不调用未定义行为的情况下使用指针访问它所指向的内容。

至于为什么存在,这对于某些算法来说很方便,并且意味着您不需要在代码中乱扔零值测试。

【讨论】:

  • @Neil: malloc(0) 在 C99 中是一样的。
  • @Alok:这完全是错误的。 C99 允许 malloc(0) 返回 0,并且好的实现会这样做。
  • @R。我认为关键是 C99 不要求实现返回0 或非0malloc(0)
【解决方案4】:

C99 标准

如果无法分配空间,则 返回空指针。如果尺寸 请求的空间为零,则 行为是实现定义的: 要么返回空指针,要么 行为就像大小一样 一些非零值,除了 返回的指针不得用于 访问一个对象。

【讨论】:

    【解决方案5】:

    comp.lang.c FAQthe following 说:

    ANSI/ISO 标准规定它可以 做任何一个;行为是 实现定义(见问题 11.33)。可移植代码必须注意不要调用 malloc(0),或者 为空值的可能性做好准备 返回。

    因此,最好避免使用malloc(0)

    【讨论】:

    • 但是 malloc(0) 返回的同一个指针,如果不是 NULL,可以被 realloc() 用来指向某个有效的内存位置。像 realloc(malloc(0), 1024);
    • @Manav:它实际上也适用于 NULL,realloc(NULL, 1024)malloc(1024) 相同
    • 能否 malloc(0) 和 realloc(malloc(0), 1024) 返回相同的指针? if (malloc(0) == realloc(malloc(0), 1024) printf("可能");
    【解决方案6】:

    还有一点没人愿意谈论,在您的第一个程序中,长度为 0 的 reallocfree 相同。

    来自 Solaris 手册页:

    realloc() 函数改变指向的块的大小 到 ptrsize 字节并返回指向(可能已移动)块的指针。内容将保持不变 新旧尺寸较小。如果ptrNULLrealloc() 对于指定的大小,其行为类似于 malloc()。如果size0ptr 不是空指针,指向的空间是做的 可供应用程序进一步分配,但 未返回系统。内存返回系统 仅在应用程序终止时。

    如果一个人不知道这可能是一个糟糕的惊喜的来源(发生在我身上)。

    【讨论】:

    【解决方案7】:

    参见 C99,第 7.20.3 节:

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

    这对所有三个分配函数都有效(即calloc()malloc()realloc())。

    【讨论】:

      【解决方案8】:

      我认为这取决于。 我检查了 Visual Studio 2005 的源代码,并在 _heap_alloc 函数中看到了这一点:

      if (size == 0)
          size = 1;
      

      我认为在许多情况下您可能需要一个有效的指针,即使是在请求零字节时也是如此。 这是因为这种一致的行为使检查指针变得更容易,因为:如果你有一个非 NULL 指针,那没关系;如果你有一个 NULL 指针,你可能有问题。 这就是为什么我认为大多数实现都会返回一个有效的指针,即使是在请求零字节时也是如此。

      【讨论】:

      • 我经常看到像size = (size+WORD_LEN)%WORD_LEN; 这样的一般调整,它使堆中的块保持字对齐,这通常会提高访问时间(尤其是块传输)。
      • 在大多数系统中,堆中的块将不仅仅是字对齐的;通常,所有块将在 8 个字节的倍数上对齐;许多将在 16 个字节的倍数上对齐。返回值必须足够好地对齐以供任何使用。对于许多系统来说,这意味着如果它用于双精度,它必须在 8 字节的倍数上对齐;对于某些系统,long double 可能是 16 个字节,依此类推。尽管英特尔芯片可能会处理未对齐的数据,但这样做会导致性能损失,而 malloc() 等人应该 - 并且应该 - 避免。
      【解决方案9】:

      如果 malloc(0) 返回虚拟指针,那么以下是如何工作的:

      void *ptr = malloc(0);

      printf("%p\n", realloc(ptr, 1024));

      我不知道您所说的“虚拟指针”是什么意思。如果malloc(0) 返回非NULL,则ptr 是指向大小为零的内存块的有效指针。 malloc 实现以特定于实现的方式保存此信息。 realloc 知道(特定于实现的)方法来确定 ptr 指向大小为零的内存块。

      (malloc/realloc/free 如何做到这一点是特定于实现的。一种可能性是分配比请求多 4 个字节并将大小存储在内存块之前。在这种情况下,((int *)ptr)[-1]将给出内存块大小,即0。你不应该在你的代码中这样做,它仅供reallocfree使用。

      【讨论】:

      • @user9876:“如果 malloc(0) 返回非 NULL,则 ptr 是指向大小为零的内存块的有效指针。”...如何分配大小为有效的内存块正如你在帖子中所说的“零”?
      • 您的用户应用程序是如何做到的?您调用 malloc(0) (如果您在返回非 NULL 的系统上)。系统是如何实现的?这有点像询问如何在磁盘上拥有一个大小为零的文件。它仍然是磁盘上的有效文件,包含所有常见的元数据(目录条目等),它的大小为零。
      猜你喜欢
      • 1970-01-01
      • 2011-03-07
      • 2017-06-13
      • 2015-06-17
      • 2012-09-02
      • 2013-05-16
      • 2012-08-09
      • 2019-12-15
      • 2017-10-08
      相关资源
      最近更新 更多