【问题标题】:memory allocation for 0 elements with calloc()?使用 calloc() 为 0 个元素分配内存?
【发布时间】:2020-03-20 22:30:18
【问题描述】:

实际上,我预计这里会出现警告/错误,但它编译时没有任何问题。为什么可以调用具有 0 个对象作为第一个参数的 calloc 函数?为什么要为此分配内存?

int* p_integer=calloc(0, sizeof(int));

if(!p_integer){
    exit(EXIT_FAILURE); 
}

//prints 4 
printf("size *p_integer: %zu\n", sizeof(*p_integer));

好的,补充一点:

void* calloc( size_t num, size_t size );


Allocates memory for an array of num objects of size and initializes all bytes in the 
allocated storage to zero.

If allocation succeeds, returns a pointer to the lowest (first) byte in the allocated 
memory block that is suitably aligned for any object type.

If size is zero, the behavior is implementation defined (null pointer may be returned, 
or some non-null pointer may be returned that may not be used to access storage) 

https://en.cppreference.com/w/c/memory/calloc

如何理解?就我而言,大小(第二个参数)不为零,对吗?因此,如果第一个参数 == 零,则无法解释这种情况。还是我必须计算 0*sizeof(int) == 0 (请求的内存块的大小)。它们是指哪个“尺寸”?

【问题讨论】:

  • 由于p_integerint* 类型,sizeof(*p_integer)sizeof(int) 相同。设置p_integer = NULL; 不会改变p_integersize
  • malloc(0)calloc(0, ...); 可能会返回 NULL 指针或非 NULL 指针。在这两种情况下:不能使用内存并且返回指针上的free() 是有效操作。
  • 所以程序应该在 p_integer=NULL 之后退出; ?但事实并非如此。
  • 编译器不需要为此发出警告。这可能会很有趣:stackoverflow.com/questions/1073157/zero-size-malloc
  • 所以程序应该在 p_integer=NULL 之后退出:它取决于实现。

标签: c


【解决方案1】:

[这个答案解决了我们希望 callocmalloc 在请求时提供零字节内存的原因。它没有解决 C 标准所说的内容。]

考虑编写一个程序来检查一些输入,并可能对各种事物进行分类,制作 C1、C2 等类别中的数据列表。之后,软件将处理 C1 的所有数据,然后处理 C2 的所有数据,以此类推。

考虑哪个更简单更短:

  • 程序为 N 个列表项分配空间并处理 N 个列表项,无论 N 是零还是正数,此代码都有效。
  • 程序必须显式测试 N 并根据 N 是零还是正数使用不同的代码路径。

前一种选择更简单、更短、更简洁。通常,它为错误提供的机会较少。当软件必须根据 N 是零还是正数而具有不同的路径时,程序员可能会忽略零的情况,从而导致错误。

【讨论】:

    【解决方案2】:

    为什么可以调用具有 0 个对象作为第一个参数的 calloc 函数? (OP)

    这是可能的,因为库的 C 规范说它是允许的。然而,0 的内存分配是导致实现定义的行为的边缘情况。

    C17/18 最近在该领域更新为:

    如果请求的空间大小为零,则行为是实现定义的:要么返回空指针以指示错误,要么行为就好像大小是某个非零值,除了返回的指针应不能用于访问对象。 C17dr § 7.22.3 1


    为什么要为此分配内存? (OP)

    没有证据表明分配了任何内存,只是为 OP 返回了一个非NULL 指针。它不能用于引用任何内存。

    我是否必须计算 0*sizeof(int) == 0(请求的内存块的大小)。它们是指哪个“尺寸”?

    “尺寸”是产品(没有size_t 范围限制)。包括sizeof(int)在内的对象的大小永远不会是0,所以只有n需要测试。对于calloc(n, sz),两者都是变量,我将使用if (n == 0 || sz == 0) 进行测试,而不是执行会导致溢出问题的乘法。


    为了避免这种实现定义的行为,首先测试参数。示例:

    int *p = (n > 0) ? calloc(n, sizeof *p) : malloc(1);
    if (p == NULL) Oops();
    ...
    free(p);
    

    或者,考虑到实现定义的行为,calloc(0, sizeof *p) 返回NULL 时可能不会出错。

    int *p = calloc(n, sizeof *p);
    if (p == NULL && n > 0) Oops();
    ...
    free(p);
    

    【讨论】:

      【解决方案3】:

      零大小的 malloc,calloc 是 100% OK。 free 可以

      p_integer*p_integer 中的sizeof 没有使用取消引用指针 - 这是 100% OK

      您可以为p_integer 分配任何值 - 但如果 malloc 或 calloc 返回有效的非 NULL 指针,则会导致潜在的内存泄漏;

      【讨论】:

        猜你喜欢
        • 2019-07-14
        • 2013-12-21
        • 2019-11-11
        • 1970-01-01
        • 2017-01-12
        • 2021-01-01
        • 2021-01-08
        • 2017-12-07
        • 1970-01-01
        相关资源
        最近更新 更多