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

When is memset to 0 nonportable?

来自this comment in GCC bug #53119

In C, {0} is the universal zero initializer equivalent to C++'s {} (the latter being invalid in C). It is necessary to use whenever you want a zero-initialized object of a complete but conceptually-opaque or implementation-defined type. The classic example in the C standard library is mbstate_t:

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

versus the common but nonportable:

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

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

memset(p, 0, n) 设置为所有位 0。
{ 0 } 的初始值设定为 0.
在几乎所有您听说过的机器上,这两个概念都是等价的。

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

另见 question 7.31 and question 5.17 in the C FAQ list


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

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

C standard 的第 6.7.9p10 节描述了字段的初始化方式如下:

If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate. If an object that has static or thread storage duration is not initialized

explicitly, then:

  • if it has pointer type, it is initialized to a null pointer;

  • if it has arithmetic type, it is initialized to (positive or unsigned) zero;

  • if it is an aggregate, every member is initialized (recursively) according to these rules, and any padding is initialized to zero bits;

  • if it is a union, the first named member is initialized (recursively) according to these rules, and any padding is initialized to zero bits

并且 p21 还指出:

If there are fewer initializers in a brace-enclosed list than there are elements or members of an aggregate, or fewer characters in a string literal used to initialize an array of known size than there are elements in the array, the remainder of the aggregate shall be initialized implicitly the same as objects that have static storage duration

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

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

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

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

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

来自here

"Possible implementation of mbstate_t is a struct type holding an array representing the incomplete multibyte character, an integer counter indicating the number of bytes in the array that have been processed, and a representation of the current shift state."

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