什么时候 memset 为 0 不可移植?

Fel*_*bek 26 c portability memset

来自GCC bug #53119 中的评论

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

mbstate_t state = { 0 }; /* correctly zero-initialized */
Run Code Online (Sandbox Code Playgroud)

与常见但不可移植的相比:

mbstate_t state;
memset(&state, 0, sizeof state);
Run Code Online (Sandbox Code Playgroud)

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

Ste*_*mit 33

memset(p, 0, n)设置为全位 0。设置为0
的初始值设定项。 在您听说过的几乎任何机器上,这两个概念是等效的。{ 0 }

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

另请参见C FAQ 列表中的问题 7.31问题 5.17


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

  • 曾经令我困惑的是,全位 0 不可能是浮点值 0.0。但是,一旦您了解了浮点值在内部的工作原理(通常使用带符号指数的偏移表示),您就会惊讶地发现所有位 0 *确实* 表示 0.0,因为指数值 0 通常表示为有偏差形成“0x80”或“0x400”。(但当然,在 IEEE-754 中,0.0 *不*以实际指数值 0 存储,所以它是有效的。) (13认同)
  • _经典_可能不恰当,但由于“mbstate_t”定义方式的可变性,它是一个_好_示例。 (2认同)
  • @ThomasWeller 对于非零指针示例,请参阅 [C FAQ 列表](http://c-faq.com/) 中的[问题 5.17](http://c-faq.com/null/machexamp.html)。我没有任何具有浮点 0.0 的非零表示形式的机器的名称,但我有充分的权威知道它们存在。 (2认同)

dbu*_*ush 5

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

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

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

明确地,那么:

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

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

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

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

p21 还指出:

如果大括号括起来的列表中的初始值设定项少于聚合的元素或成员,或者用于初始化已知大小的数组的字符串文字中的字符少于数组中的元素,则聚合的其余部分应与具有静态存储持续时间的对象一样隐式初始化

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

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

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