c - 什么构成联合中的填充?
问题描述
我试图解释C11 标准关于在未明确初始化时联合的静态(和线程本地)初始化。
第 6.7.9 10 节(第 139 页)规定如下:
如果具有自动存储持续时间的对象未显式初始化,则其值是不确定的。如果具有静态或线程存储持续时间的对象未显式初始化,则:
— 如果它具有指针类型,则将其初始化为空指针;
— 如果它具有算术类型,则将其初始化为(正或无符号)零;
— 如果是聚合,则每个成员都根据这些规则(递归地)初始化,并且任何填充都被初始化为零位;
— 如果是联合,则根据这些规则(递归)初始化第一个命名成员,并将任何填充初始化为零位;
假设我们在 amd64 架构上,给定以下语句:
static union { uint32_t x; uint16_t y[3]; } u;
可以u.y[2]
包含非零值还是初始化为零,因为它被视为填充?
我已经搜索了 C11 标准,但几乎没有解释什么构成联合中的填充。在C99 标准(第 126 页)中没有提到填充,所以在这种情况下u.y[2]
可以是非零的。
解决方案
y
未被使用的额外空间x
不被视为填充。C11 标准中关于“结构和联合说明符”的第 6.7.2.1p17 节规定:
结构或联合的末尾可能有未命名的填充
您的示例中使用的字节y
未被使用,x
仍然被命名,因此不是填充。
您的示例很可能确实有这个未命名的填充,因为最大的成员占用 6 个字节,但其中一个成员是 auint32_t
通常需要 4 个字节对齐。事实上,在 gcc 4.8.5 上,这个联合的大小是 8 个字节。所以这个 union 的内存布局是这样的:
----- --| ---|
0 | 0 | | |
----- | |-- y[0]
1 | 0 | | |
----- |-- x ---|
2 | 0 | | |
----- | |-- y[1]
3 | 0 | | |
----- --| ---|
4 | 0 | |
----- |-- y[2]
5 | 0 | |
----- ---|
6 | 0 | -- padding
-----
7 | 0 | -- padding
-----
因此,严格阅读标准,对于没有显式初始化程序的此联合的静态实例:
- 对应于
x
(即第一个命名成员)的字节 0 - 3 被初始化为 0,结果x
为 0。 - 对应于 y[2] 的字节 4 - 5 保持未初始化并具有不确定的值。
- 字节 6 - 7,对应于填充,被初始化为 0。
我在 gcc 4.8.5、clang 3.3 和 MSVC 2015 上对此进行了测试,它们都在各种优化设置下将所有字节设置为 0。但是,严格阅读标准并不能保证行为,因此这些编译器的不同优化设置、它们的不同版本或完全不同的编译器可能会做不同的事情。
从实用的角度来看,编译器只需将静态对象的所有字节设置为 0 即可满足此要求。这当然是假设整数类型没有填充,浮点类型是 IEEE754,NULL 指针的数值为 0。在大多数人可能遇到的大多数系统上,情况都是如此。不是这种情况的系统可能更有可能将这些字节设置为 0 以外的值。所以同样,虽然这些字节可能设置为 0,但不能保证。
要记住的重要一点是,根据 6.7.2.1p16,联合一次只能存储一个成员:
工会的规模足以容纳其最大的成员。 任何时候最多可以将其中一个成员的值存储在联合对象中。 一个指向联合对象的指针,经过适当的转换,指向它的每个成员(或者如果一个成员是位域,则指向它所在的单元),反之亦然。
因此,如果union
具有静态存储持续时间的 a 未初始化,则仅访问第一个成员是安全的,因为这是隐式初始化的成员。
唯一的例外是如果联合包含具有一组公共初始成员的结构,在这种情况下,您可以访问内部结构的任何公共元素。这在第 6.5.2.3p6 节中有详细说明:
一个特殊的保证是为了简化联合的使用:如果联合包含多个共享相同初始序列的结构(见下文),并且如果联合对象当前包含这些结构之一,则允许检查公共它们中的任何一个的初始部分,在任何地方都可以看到已完成联合类型的声明。如果对应的成员对于一个或多个初始成员的序列具有兼容的类型(并且对于位域,具有相同的宽度),则两个结构共享一个共同的初始序列。
推荐阅读
- c# - 如何从代码优先实体框架中的不可空列中删除默认值
- ios - 在 IOS 中使用 AUGraph 在 AudioUnit 中播放立体声
- python - 从现有值中添加值,Python 字典
- javascript - vue-ctk-date-time-picker 2 如何将下拉位置更改为正确?
- cron - 启用 SELinux 的 NFS 共享 Oracle Linux 8 集群上的 Cron 作业失败
- javascript - 如何在 React 中同时构建受控和不受控的 Input 组件?
- nuxt.js - 用于特定路由的 NuxtJS 路由中间件
- r - R - 滚动窗口上的产品总和(四分之一)
- flutter - 导航计入颤动
- windows - Windows 上的电子吐司操作