我最近遇到了一位同事的代码,如下所示:
typedef struct A {
int x;
}A;
typedef struct B {
A a;
int d;
}B;
void fn(){
B *b;
((A*)b)->x = 10;
}
他的解释是,由于 struct A
是 struct B
的第一个成员,所以 b->x
与 b->a.x
并提供更好的可读性。
这是有道理的,但这被认为是好的做法吗?这会跨平台工作吗?目前这在 GCC 上运行良好。
最佳答案
是的,它可以跨平台工作(a),但这不一定是个好主意。
根据 ISO C 标准(以下所有引用均来自 C11),6.7.2.1 Structure and union specifiers/15
,不允许在之前进行填充结构的第一个元素
此外,6.2.7 兼容类型和复合类型
指出:
Two types have compatible type if their types are the same
毫无疑问,A
和 A-within-B
类型是相同的。
这意味着对 A
字段的内存访问在 A
和 B
类型中将是相同的,更明智的是b->a.x
如果您对将来的可维护性有任何顾虑,这可能是您应该使用的。
而且,虽然您通常不得不担心严格的类型别名,但我认为这不适用于此处。别名指针是非法的,但标准有特定的异常(exception)。
6.5 Expressions/7
声明了其中一些异常(exception)情况,并附有脚注:
The intent of this list is to specify those circumstances in which an object may or may not be aliased.
列出的异常(exception)是:
与对象的有效类型兼容的类型
;- 其他一些我们在这里不必关心的异常(exception)情况;和
在其成员中包含上述类型之一的聚合或 union 类型(递归地包括子聚合或包含的 union 的成员)
。
那,结合上面提到的struct padding规则,包括短语:
A pointer to a structure object, suitably converted, points to its initial member
似乎表明这个例子是特别允许的。这里我们要记住的核心点是表达式((A*)b)
的类型是A*
,不是B*
.这使得变量兼容无限制的别名。
那是我对标准相关部分的解读,我在(b)之前就错了,但我对此表示怀疑。
因此,如果您有真正的需求,它可以正常工作,但我会在代码中非常记录与结构接近的任何约束,以便以免以后被咬。
(a) 一般意义上的。当然是代码片段:
B *b;
((A*)b)->x = 10;
将是未定义的行为,因为 b
没有被初始化为合理的东西。但我假设这只是用于说明您的问题的示例代码。如果有人担心它,请将其视为:
B b, *pb = &b;
((A*)pb)->x = 10;
(b) 正如我妻子会经常告诉你的那样 :-)
关于c - 在 C 中扩展结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22218604/