如果记录类型 T1 和 T2 的第一个 k 字段相同,这些字段是否保证具有相同的偏移量?例如,这个程序定义明确吗?
typedef struct {
int x, y;
} Shape;
typedef struct {
int x, y;
int w, h;
} Rectangle;
static void InitShape(Shape *s)
{
s->x = 0;
s->y = 0;
}
int main(void)
{
Rectangle r;
InitShape((Shape *) &r);
r.w = 1;
r.h = 1;
return 0;
}
编辑:在我的项目中生成C代码并包含 Rectangle 中 Shape 的各个字段(而不是单个字段 base 类型 Shape )简化了其他地方的代码。
答案 0 :(得分:3)
程序未定义。
C标准仅保证在结构的第一个成员之前不存在填充。这意味着结构Shape和Rectangle的成员x和y之间可能存在不同数量的填充,并且当一个Rectangle类型的对象被重新解释为Shape类型时,它可能没有成员处于相同的偏移量。
这不是主要问题,因为期望两个成员具有相同的偏移量是合理的,并且这可以使用静态断言来强制执行。
主要问题是严格别名。在某些情况下,类型可能会将 1 作为别名,但这两种类型不满足任何类型。 Rectangle和Shape类型不兼容,Rectangle类型不包含Shape类型的成员。
因此将类型Rectangle重新解释为Shape:InitShape((Shape *) &r);
类型会导致未定义的行为。
1 6.5表达式7
对象的存储值只能由具有其中一个的左值表达式访问
以下类型:
- 与对象的有效类型兼容的类型,
- 聚合或联合类型,其中包括上述类型之一
成员(包括递归地,子集合或包含的联合的成员)
答案 1 :(得分:0)
它会起作用。该函数将初始化结构的x和y字段,就好像它是一个形状一样。我已经看过许多使用类似东西的项目,他们用相同的第一个字段创建了几个结构来区分它们(看看由quel solaar编写的爱情游戏)如果你明智地使用它,它实际上非常有用。
但是我会采用不同的方式。当初始数据相同时,我实际上修改了这样的Rectangle结构:
valve
这样,当你调用init函数时,你可以将矩形强制转换为一个形状,或者将内部Shape传递给函数。在我看来它更清楚。
更进一步,我会做更类似的事情:
pyramid
更好的是我使用枚举而不是类型的定义,并且字段将更改为枚举,但是对于这个大小,我认为这样就可以了。
但是,再次回答你的问题, IF 矩形和形状的x和y字段意味着相同的事情,那么你的方式将毫无问题地工作。
PS:我没有编译上面的内容来测试它是否有效,只是为了给出一个想法。
答案 2 :(得分:-1)
C99标准添加(并且C11保留)要求使用通用初始序列保证的代码必须确保在访问所涉及的结构时必须可见的完整联合类型,以便让编译器知道它们可能是别名。
标准不要求使用联合类型执行访问,但是gcc的作者要么相信也要假装它,尽管事实上没有理由指定完成联合类型在这种解释下是可见的,尽管这样的解释使得共同初始序列保证几乎毫无价值。
我建议添加union
类型声明,以确保与实际遵循标准的编译器兼容,但我也建议如果您使用gcc,则无论如何使用-fno-strict-alias
, gcc的作者公然拒绝以符合标准的方式处理严格符合规范的代码。