C标准下类型双关与联合类型访问及转换的合规性问询
背景回顾
C标准(ISO/IEC 9899:201x)的6.5.2.3.9例3给出了利用联合公共初始序列的合法代码:
#include<stdio.h> union point { struct { int value; } x; struct { int value; } y; }; int main() { union point p; p.x.value = 1; printf("%d\n", p.y.value); }
对应的标准条款翻译如下:
为简化联合体的使用,标准做出一项特殊保证:如果联合体包含若干共享公共初始序列的结构体(定义见下文),且当前联合体对象中存储的是这些结构体之一,那么只要联合体的完整类型声明可见,就允许检查任意一个此类结构体的公共初始部分。当两个结构体的一段或多段初始成员具有兼容类型(对于位域,还要求宽度相同)时,它们共享公共初始序列。
问题1:如下代码是否符合C标准规定?
#include<stdio.h> union point { int x; int y; }; int main() { union point p; p.x = 1; printf("%d\n", p.y); }
完全符合C标准规定。可以将两个int成员视为仅含单个成员的公共初始序列,满足“对应成员类型兼容”的要求。此外,联合的所有成员共享同一存储空间,给p.x赋值后,读取同类型的p.y本质是直接访问该存储区域的int值,这是标准明确允许的操作。
问题2:标准中是否有条款保证对象表示完全依赖于对象类型,从而确保上述代码合法?
是的。C标准(如ISO/IEC 9899:2011的6.2.6.1)规定:同一类型的所有对象具有相同的对象表示(二进制存储格式)。联合的成员共享存储空间,p.x和p.y都是int类型,因此它们的对象表示完全一致,读取p.y就是读取该存储区域的合法int值,标准对此有明确保证。
问题3:该规则是否适用于更复杂的类型(如struct或union)?
适用于符合条件的复杂类型。只要联合中的结构体成员共享公共初始序列,就可以合法跨成员访问。例如:
union demo { struct { int a; char b; } s1; struct { int a; double c; } s2; };
s1和s2的第一个成员都是int a,属于公共初始序列,给demo.s1.a赋值后,读取demo.s2.a完全符合标准。
对于联合类型作为成员的情况,只要成员类型兼容或其公共初始序列符合要求,规则同样适用。但如果复杂类型的初始成员类型不兼容,跨成员访问就不符合标准。
问题4:给定如下联合定义,是否可以编写一个签名为union second convert(union first input)的纯函数,使其符合C标准,且无需记住union first中存储的成员类型,即可将匹配成员映射到union second中?
union first { int a; char b; }; union second { int a; char b; float c; };
可以编写这样的纯函数。union first和union second的前两个成员类型完全兼容,联合的存储共享特性保证了union first的对象表示可以直接对应到union second的前半部分存储。
示例实现:
#include <string.h> union second convert(union first input) { union second res; // 直接拷贝整个union first的存储内容到union second memcpy(&res, &input, sizeof(union first)); return res; }
无论input中存储的是a还是b,拷贝后res.a或res.b都能正确读取对应的值——因为两者的同类型成员对象表示完全一致,无需额外记录输入的成员类型。
内容的提问来源于stack exchange,提问作者Maksymilian Mika

