相同字段序列中的字段是否具有相同的偏移量?

Do fields in identical field sequences have the same offset?

如果记录类型 T1T2 的前 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 代码,并将 Shape 中的各个字段包含在 Rectangle 中(而不是单个字段base 类型 Shape) 简化了别处的代码。

它会起作用的。该函数将初始化结构的 x 和 y 字段,就好像它是一个形状一样。我见过很多项目使用类似的东西,他们创建了几个具有相同第一个字段的结构来区分它们(看看 love game,由 quel solaar 编写)如果你明智地使用它实际上非常有用.

但是我会做不同的事情。当初始数据相同时,我实际上会像这样修改 Rectangle 结构:

typedef struct { 
    Shape s;
    int w, h;
} Rectangle;

这样,当您调用 init 函数时,您可以将矩形类型转换为形状或将内部 Shape 传递给该函数。在我看来,它使它更清楚。

更进一步,我会做一些与此类似的事情:

#include <stdint.h>

#define TYPE_SHAPE 0U
#define TYPE_RECTANGLE 1U

typedef struct {
    char *name;
    char *descrition;
    uit8_t type;
} Info;

typedef struct {
    Info info;
    int x, y;
} Shape;

typedef struct {
    Info info;
    Shape s;
    int w,h;
}

void Init(Info *block) {
    if(block->type == TYPE_SHAPE) {
        //initialize it
    } else {
        //do something elese
    }
}

更好的是,我会使用枚举而不是类型的定义,并且该字段将更改为枚举,但对于这个大小,我认为这样就可以了。

不过,再次回答你的问题,如果矩形和形状的 x 和 y 字段表示相同的东西,那么你的方法将没有任何问题。

PS: 上面的我没有编译测试是否有效,仅供参考。

程序未定义。

C 标准只保证在结构的第一个成员之前没有填充。这意味着结构 Shape 和 Rectangle 的成员 x 和 y 之间可能存在不同的填充量,并且当一个 Rectangle 类型的对象被重新解释为 Shape 类型时,它可能没有相同偏移量的成员。

这不是主要问题,因为可以合理地预期两个成员将具有相同的偏移量,并且这可以使用静态断言来强制执行。

主要问题是严格的别名。在某些情况下,类型可能会别名 1,但这两种类型都不满足其中任何一个。 Rectangle 和 Shape 类型不兼容,并且 Rectangle 类型不包含 Shape 类型的成员。

因此将 Rectangle 类型重新解释为 Shape 类型:InitShape((Shape *) &r);,导致未定义的行为。


1 6.5 表达式 7
对象的存储值只能由具有以下之一的左值表达式访问 以下类型:
— 与对象的有效类型兼容的类型,
— 聚合或联合类型,其中包括上述类型之一 成员(递归地包括子聚合或包含联合的成员)

C99 标准添加(并且 C11 保留)一项要求,即使用公共初始序列保证的代码必须确保具有所涉及结构的完整联合类型在访问时必须可见,以便让编译器知道他们可能会使用别名。

标准不要求使用联合类型执行访问,但 gcc 的作者相信或假装它确实如此,尽管事实上没有理由指定 complete 联合类型在这样的解释下是可见的,尽管事实上这样的解释使公共初始序列保证几乎毫无价值。

我建议添加一个 union 类型声明以确保与实际遵循标准的编译器兼容,但我也建议如果您使用的是 gcc,则无论如何都使用 -fno-strict-alias,因为gcc 的作者公开拒绝以符合标准的方式处理严格符合标准的代码。