union vec
{
#pragma pack(push,1)
struct
{
float x, y, z;
}
#pragma pack(pop)
float vals[3];
};
考虑上面的定义。 (C99中的匿名工会除外)
我认为这个答案可能允许不同的答案,具体取决于编译器的选择,语言的选择和标准的选择。
sizeof(vec) == 3*sizeof(float)
&vec.x == &vec.vals[0]
,等等。v.x
写入然后从v.vals[0]
读取包装旁边,我认为相关的措辞(至少从C99标准)是:
与对象的有效类型兼容的类型
包含上述内容之一的聚合或联合类型 其成员之间的类型(包括,递归地,成员 subaggregate或contained union),或
答案 0 :(得分:6)
- 我相信我保证(通过#pragma编译器文档,而不是语言保证)sizeof(vec)== 3 * sizeof(float)
醇>
是的,这是正确的,假设完全禁用#pragma
填充。
- 因此,我相信我保证& vec.x ==& vec.vals [0],等等。
醇>
无论填充如何,都可以保证这一点,因为在struct / union的开头永远不会有填充。参见例如C116.7.2.1§15:
结构对象中可能有未命名的填充,但不在其开头。
这适用于C标准的所有版本,据我所知,也适用于所有版本的C ++标准。
- 但是,我不确定它是否合法(即,不允许通过严格别名),从v.x写入然后从v.vals [0]
读取 醇>
这在C中很好,但在C ++中是未定义的行为。
在C中,.
/ ->
运算符保证了这一点,C11 6.5.2.3:
后缀表达式后跟。运算符和标识符指定的成员 结构或联合对象。该值是指定成员的值,95)如果第一个表达式是左值,则它是左值。
脚注95(信息性而非规范性)说:
95)如果用于读取union对象内容的成员与上次使用的成员不同 在对象中存储一个值,该值的对象表示的相应部分被重新解释 作为6.2.6中描述的新类型中的对象表示(有时称为''类型的过程) 双关语””)。这可能是陷阱表示。
C ++没有这样的保证,因此通过联合的“类型惩罚”是C ++中未定义的行为。这是两种语言之间的主要区别。
此外,C对于联合也有公共初始序列的概念,也在C11 6.5.2.3中指定:
为了简化工会的使用,我们提出了一项特殊保证:如果工会包含 几个结构共享一个共同的初始序列(见下文),如果是联盟 对象当前包含这些结构中的一个,允许检查公共结构 其中任何一个的初始部分都是完整类型的联盟的声明 是可见的。如果相应的成员具有一个或多个序列的兼容类型(并且对于位字段,具有相同的宽度),则两个结构共享公共初始序列 初始成员。
确实,您的示例中的数组和结构可能是别名,因为您引用的部分是“在其成员中包含上述类型之一的聚合或联合类型”。因此,写入结构然后通过数组读取数据不会违反严格的别名,无论是C还是C ++。
然而,C ++在处理联合时具有“活动成员”的概念,因此在C ++中,除了别名之外,由于其他原因,这会给出指定不当的行为 - 即C ++只保证最后一个工会的书面成员可以安全地阅读。