【问题标题】:When is memset to 0 nonportable? [duplicate]memset 何时为 0 不可移植? [复制]
【发布时间】:2022-01-07 04:58:25
【问题描述】:

来自this comment in GCC bug #53119

在 C 中,{0} 是通用零初始化器,等效于 C++ 的 {}(后者在 C 中无效)。每当您想要一个完整但概念上不透明或实现定义类型的零初始化对象时,都必须使用它。 C标准库中的经典例子是mbstate_t

mbstate_t state = { 0 }; /* correctly zero-initialized */

相对于普通但不便携:

mbstate_t state;
memset(&state, 0, sizeof state);

让我感到奇怪的是,后一个版本可能是不可移植的(即使对于实现定义的类型,编译器也必须知道大小)。这里有什么问题,memset(x, 0, sizeof x) 何时不可移植?

【问题讨论】:

    标签: c portability memset


    【解决方案1】:

    memset(p, 0, n) 设置为 all-bits-0。
    { 0 } 的初始值设定项设置为 0。
    在您听说过的任何机器上,这两个概念都是等价的。

    但是,有些机器的浮点值 0.0不是由全位 0 的位模式表示。并且有些机器也没有用全位 0 的位模式表示空指针。在这些机器上,{ 0 } 的初始化程序总是会为您提供所需的零初始化,而 memset 可能不会。

    另请参阅C FAQ list 中的question 7.31question 5.17


    后记:不过,我不清楚为什么mbstate_t 会成为这个问题的“经典例子”。


    P.P.S. @ryker 指出的另一个区别:memset 会将填充结构中的任何“孔”设置为 0,而将该结构设置为 { 0 } 可能不会。

    【讨论】:

    • 我正要写同样的。我在苏联计算机上工作,其中所有单词都设置为零的浮点数是一个陷阱表示:)
    • 曾经让我困惑的是 all-bits-0 不可能是浮点值 0.0。但是一旦你了解了浮点值是如何在内部工作的,通常使用有符号指数的偏移量表示,那么 all-bits-0 确实 表示 0.0 有点令人惊讶,因为指数值 0 是通常表示为有偏的形式0x800x400。 (但当然,在 IEEE-754 0.0 中存储实际指数值为 0,因此可以解决。)
    • Classic 可能不合适,但由于 mbstate_t 的定义方式不同,这是一个的例子。跨度>
    • @ThomasWeller 对于非零指针示例,请参阅C FAQ list 中的question 5.17。对于具有浮点 0.0 的非零表示的机器,我没有任何名称,但我认为它们存在的权威性很高。
    • @benrg 关于单调性的好点。另一种看待它的方式是 0.0 非常好地位于次正规线中,它肯定属于它,因为它显然也没有隐含的“1”位。
    【解决方案2】:

    原因与类型的表示方式有关。

    C standard 的第 6.7.9p10 节描述了如何初始化字段,如下所示:

    如果具有自动存储持续时间的对象不是 显式初始化,它的值是不确定的。如果一个对象 具有静态或线程存储持续时间的未初始化

    明确地说:

    • 如果是指针类型,则初始化为空指针;

    • 如果它具有算术类型,则初始化为(正或无符号)零;

    • 如果是聚合,则每个成员都根据这些规则进行初始化(递归),并且任何填充都初始化为零位;

    • 如果是联合,则根据这些规则(递归)初始化第一个命名成员,并初始化任何填充 归零位

    而且 p21 还指出:

    如果大括号括起来的列表中的初始值设定项比那里少 是聚合的元素或成员,或 用于初始化已知数组的字符串文字 大小比数组中的元素多,其余的 合计应为 隐式初始化与具有静态存储的对象相同 持续时间

    这与将所有字节设置为零之间的区别在于,上述某些值可能不一定由所有位为零来表示。

    例如,有些架构中地址 0 是有效地址。这意味着空指针不表示为所有位为零。 (注意:(void *)0 被标准指定为空指针常量,但实现将其视为空指针的表示形式)

    该标准也没有规定浮点类型的特定表示。虽然最常见的表示形式 IEEE754 确实使用所有位 0 来表示值 +0,但对于其他表示形式不一定如此。

    【讨论】:

    • "that a NULL pointer" 不如 "that a null pointer"(小写)。 空指针常量 NULL 带来了一系列困难。 NULL 可能是 int 0,但 空指针 可能与全零位不同。
    【解决方案3】:

    注意两种方法之间的行为差​​异...

    ...= {0};中如果存在填充字节,则不会被清除。
    但是调用memset() 将清除填充。

    来自here

    "mbstate_t 的可能实现是一个结构类型,它包含一个表示不完整多字节字符的数组,一个整数 计数器指示数组中已被读取的字节数 已处理,以及当前班次状态的表示。”

    如果mbstate_t 实现为struct,值得注意的是{0} 不会将可能存在的填充字节设置为zero,因此以下假设值得商榷:

    mbstate_t state = { 0 }; /* correctly zero-initialized */
     
    

    memset() 但是确实包含填充字节。

    memset(state , 0, sizeof state);//all bytes in memory region of test will be cleared
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-05-01
      • 2011-09-22
      • 2014-08-31
      • 1970-01-01
      • 2010-09-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多