u.y[2]
是否可以包含非零值或者它被初始化为零是因为视为填充而不是元素?
u.y[2]
并不被视为填充。它是数组y
的一个元素,该数组是联合体u
的成员。
该联合体的大小仅足以容纳其最大的成员(也可能添加未命名的尾部填充以实现对齐)。
来自C标准#6.7.2.1p17
17 结构体或联合体的末尾可能有未命名的填充。
联合体u
的最大成员是uint16_t y [3];
。因此,如果联合体u
中有任何填充,则会在uint16_t y [3];
成员之后发生1)。
根据C11标准,具有静态或线程存储期且未显式初始化的联合对象,编译器应递归地初始化第一个命名成员和任何填充为零位。因此,您不应该对
u.y [2]
值做出任何假设,因为编译器只会初始化联合体中的
第一个命名成员(递归),这在您的示例中是
uint32_t x
,并将任何填充为零位(#6.7.9p10)。
C标准没有提及数据段(初始化/未初始化)、堆栈、堆等。所有这些都是特定于架构/平台的。对于对象初始化,C标准仅指定要初始化为
0
和不要初始化的内容,并且不指定哪个存储期对象进入哪个段。标准规范适用于编译器,好的编译器应遵循它们。通常,初始化为
0
的静态数据放在.BSS(由符号块启动),非
0
初始化数据放在.DATA(数据段)中。因此,您可能会发现
u.y [2]
值为
0
,但这并不总是正确的情况。
1) 每个现代编译器都会根据架构自动使用数据结构填充。一些编译器甚至支持警告标志-Wpadded
,它会生成有关结构填充的有用警告。这些警告帮助程序员在需要更有效的数据结构布局时进行手动处理。
-Wpadded
如果在结构中包含填充以对齐结构的元素或整个结构,则发出警告。有时,可以重新排列结构的字段以减少填充并使结构更小。
因此,如果您的编译器支持警告标志-Wpadded
,请尝试使用它编译代码。这将帮助您了解编译器包含的填充。
例如:
#include <inttypes.h>
int main() {
static union { uint32_t x; uint16_t y[3]; } u;
}
让我们使用-Wpadded
选项编译这个程序。我的编译器是clang
版本clang-1000.10.44.4
# clang -Wpadded p.c
p.c:4:16: warning: padding size of 'union (anonymous at p.c:4:16)' with 2 bytes to alignment boundary [-Wpadded]
static union { uint32_t x; uint16_t y[3]; } u;
^
1 warning generated.
2) 需要注意的一点是 - 如果您明确初始化一个联合对象,除非它是指定初始化,否则联合的第一个成员也将被初始化(C11标准#6.7.9p17)。
u.y[3]
吗?我的理解是,如果联合需要一些填充,它将被初始化为0。假设在您的架构上,u需要4字节对齐,而y [3]是6字节并需要2字节对齐,则最终对齐需要是8的倍数,因此您将有2字节的填充(实际上在u.y [3]处),这将是0。 - Phil1970u.y[3]
一样。作为C实现的使用者,我必须将这个问题所询问的段落解释为告诉我尾部填充被初始化为零,并且没有告诉我y
中而不是x
中的字节(因此u.y[2]
)被初始化为零。作为实现开发人员,我可能会采取预防措施,将所有字节都初始化为零。 - Eric Postpischil