是的,它可以在不同平台上工作(a),但这并不一定意味着这是一个好主意。
根据ISO C标准(以下引用均来自C11),6.7.2.1 结构和联合说明符/15
,结构体的第一个元素之前不允许有填充。
此外,6.2.7 兼容类型和复合类型
指出:
如果两种类型相同,则这两种类型具有兼容类型
而且 A
和 B
类型是完全相同的事实是无可争议的。
这意味着对于 A
和 B
类型访问 A
字段的内存访问将是相同的,更明智的做法可能是使用 b->a.x
,如果您担心将来的可维护性,这可能是您应该使用的。
虽然您通常需要担心严格的类型别名,但我认为在这里不适用。虽然引用指针被认为是非法的,但标准有特定的例外。
6.5 表达式/7
列出了其中一些例外情况,并附注:
此列表的目的是指定对象可能或不可能别名的那些情况。
列出的例外情况包括:
与对象的有效类型兼容的类型
;
- 其他一些例外情况,这里不需要关心;
包含前述类型之一的聚合体或联合体类型(包括子聚合体或包含联合体的成员)
。
结合上面提到的结构填充规则,包括该短语:
指向结构对象的指针经过适当转换后指向其初始成员
似乎表明此示例是特别允许的。我们必须记住的核心要点是表达式 ((A*)b)
的类型是 A*
,而不是 B*
。这使得变量在无限制别名的目的下是兼容的。
这是我对标准相关部分的理解,我以前也犯过错误(b),但在这种情况下我不太可能会犯错。
因此,如果您真正需要这样做,它将可以正常工作,但我会在代码中非常接近结构文档中记录任何约束,以免在将来受到伤害。
(a) 在一般意义上。当然,代码片段:
B *b;
((A*)b)->x = 10;
由于b
未初始化为合理值,这将是未定义行为。但我假设这只是示例代码,旨在说明你的问题。如果有人对此表示担忧,请考虑将其改为:
B b, *pb = &b;
((A*)pb)->x = 10;
(b)正如我的妻子经常且很少需要提示地告诉你的那样 :-)