【问题标题】:Is it common practice to memset reallocated memory to 0?将 memset 重新分配的内存设置为 0 是常见的做法吗?
【发布时间】:2015-09-23 06:27:28
【问题描述】:

在一本 C 书籍中,我在一个实现动态调整大小数组的示例中找到了这段代码(简化):

void *contents = realloc(array->contents, array->max * sizeof(void *));
array->contents = contents;

memset(array->contents + old_max, 0, array->expand_rate + 1);

来源:Learn C The Hard Way – Chapter 34

我有点惊讶memset 应该在这里实现什么,但后来我明白它是用来“清零”重新分配的内存。

我用谷歌搜索了,如果这是我在realloc 之后应该做的事情,并找到了关于这个的 stackoverflow 答案:

可能没有必要做memset […]

但是,即使您想“将其归零以使一切都很好”,或者确实需要将新指针设为NULL:C 标准并不保证所有位为零是空指针常量(即NULL),所以memset() 无论如何都不是正确的解决方案。

来源:How to zero out new memory after realloc

建议的解决方案是使用for 循环而不是memset,以便将内存设置为NULL

所以我的问题是,memset 并不一定意味着将值设置为NULL,而for 循环解决方案似乎有点乏味——真的需要设置新分配的内存吗?

【问题讨论】:

  • IMO 这相当于询问callocmalloc。如果您使用calloc 分配和归零内存,那么如果您需要使用realloc 增加缓冲区,那么也使用memset 是一致的。
  • 我觉得奇怪的是,您读到需要使用 for 循环,将每个值都设置为 NULL 而不仅仅是将内存归零。我认为从技术上讲 NULL 不必等于 0x0。但是,在实践中,您将很难找到不这样做的编译器/系统。 (有人可以备份吗?)
  • 根据某些人的说法,Learn C the Hard Way 是not a great resource,我倾向于同意。考虑至少阅读一些其他资源,例如来自this answer SO C 书单。请注意,如果您决定阅读 K&R,请用一只手阅读,另一只手阅读勘误表。
  • @RoyT。 NULL has to compare equal to 0 but does not have to be all zero bits,尽管在实践中通常是这样。
  • @RoyT。 - 它发生在一些没有内存保护的旧系统上。物理地址 0 可以包含操作系统信息,例如中断处理程序。写入空指针会覆盖这是一个糟糕的想法。在这种情况下,让 NULL 指向 ROM 而不是 RAM 可能会更好。

标签: c memory idioms


【解决方案1】:

所以我的问题是,因为 memset 并不一定意味着设置值 为 NULL 并且 for 循环解决方案似乎有点乏味 - 真的吗 需要设置新分配的内存吗?

realloc 不会初始化新分配的内存段的值。

因此,如果您打算读取该(未初始化的)内存的值,则需要初始化内存。因为从那个未初始化的内存中读取值会触发未定义的行为。

顺便说一句,使用realloc(因为它可能会失败)的安全方法是:

  // Since using realloc with size of 0 is tricky and useless probably
  // we use below check (from discussion with @chux)
  if (new_size == 0) 
    dosmth();
  else 
  {
    new_p = realloc(p, new_size);
    if (new_p == NULL)
    {
      // ...handle error
    }else
    {
      p = new_p;
    }
  }

【讨论】:

  • 我认为初始化内存很有意义,因为初始数组是用calloc分配的。
  • @Max:好吧,如前所述,分配内存的“新部分”不会被初始化,如果你尝试从该内存中读取值,你将触发 UB
【解决方案2】:

void * 设置为0 是一个比较等于NULL 的值。可能memset(ptr, 0, size) 没问题 - 但需要查看更多代码才能确定。

OTOH:代码正确吗?也许应该是

// memset(array->contents + old_max, 0, array->expand_rate + 1);
memset(array->contents + old_max, 0, sizeof(void *) * (array->expand_rate + 1) );

【讨论】:

    【解决方案3】:

    这是错误的:

    但是,即使您想“将其归零,以便一切顺利”,或者 确实需要新指针为 NULL:C 标准没有 保证所有位为零是空指针常量(即, NULL),所以 memset() 无论如何都不是正确的解决方案。

    C 标准实际上确实保证了这一点。

    根据C standard 的第 6.3.2.3 节:

    值为 0 的整数常量表达式,或这样的表达式 转换为void * 类型,称为空指针常量。如果一个空 指针常量转换为指针类型,结果 指针,称为空指针,保证比较不等于 指向任何对象或函数的指针。

    将空指针转换为另一种指针类型会产生空指针 该类型的指针。任何两个空指针应该比较相等。

    请注意,值为 0 的指针是 a 空指针。这并不意味着NULL 本身必须为零。但也要注意“任何两个空指针应该比较相等”。所以任何空指针都等于NULL的值。

    由于memset() 将整数值作为其第二个参数,将零整数值传递给memset() 将产生空指针。因为传递给memset() 的值是“[a]n 值为 0 的整数常量表达式”,根据 6.3.2.3。

    【讨论】:

    • 这个答案是倒退的。宏NULL 保证为0 或(void*) 0:空指针常量。它的表示(这是编译器在指针上下文中将0 转换为的内容)不一定全为零,因此memset 不保证产生空指针。当您将0 作为memset 的参数时,它在指针上下文中是not,因此它不会被转换为空指针。另见c-faq.com/null/index.html
    • "宏NULL保证为0或(void*)0" 权威引用?
    • 来自 ISO C99 规范,第 6.3.2.3 节(您引用了):“值为 0 的整数常量表达式,或转换为类型 void * 的此类表达式,称为 空指针常量 [55]。”结合脚注 55:“宏 NULL 被定义为......作为一个空指针常量......”
    • 此外,您引用的部分指出“如果空指针 constant转换 为指针类型,则生成的指针称为 空指针...”(强调我的)。您的答案将空指针 constant(又名 NULL,又名 0)与空指针(不一定全为零)混合在一起。
    • 你根本不懂标准。 A 空指针常量不是THE 空指针常量。鉴于标准规定任何两个空指针应该比较相等,显然可以有多个空指针常量。标准中没有任何内容要求任何给定的 空指针常量 应为“[a]n 值为 0 的整数常量表达式”。它所做的只是要求将整数值 0 视为 空指针常量。没有办法将其中的任何内容解读为“NULL 必须为零”。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-27
    • 1970-01-01
    • 1970-01-01
    • 2011-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多