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