匿名结构的初始化,gcc 4.9 的解决方法

Initialization of anonymous struct, workaround for gcc 4.9

我有以下结构类型:

typedef struct PG_Point PG_Point;
struct PG_Point
{
    int x;
    int y;
};

typedef struct PG_Size PG_Size;
struct PG_Size
{
    int width;
    int height;
};

typedef struct PG_Bounds PG_Bounds;
struct PG_Bounds
{
    union
    {
        struct
        {
            PG_Point topLeft;
            PG_Size size;
        };
        struct
        {
            struct
            {
                int x;
                int y;
            };
            struct
            {
                int width;
                int height;
            };
        };
    };
};

使用以下初始值设定项:

#define PG_Point_init(ix, iy) {.x=(ix), .y=(iy)}
#define PG_Size_init(iwidth, iheight) {.width=(iwidth), .height=(iheight)}

#define PG_Bounds_init(ix, iy, iwidth, iheight) { \
    .topLeft=PG_Point_init((ix),(iy)), \
    .size=PG_Size_init((iwidth),(iheight)) }

据我了解,在 中初始化匿名结构的字段就好像它们是包含结构的直接字段一样正确吗?但是对于 gcc 4.9.2,这会给出以下警告:

warning: missing initializer for field ‘size’ of ‘struct <anonymous>’ [-Wmissing-field-initializers]

如果我将初始值设定项更改为此版本,它会起作用:

#define PG_Bounds_init(ix, iy, iwidth, iheight) {{{ \
    .topLeft=PG_Point_init((ix),(iy)), \
    .size=PG_Size_init((iwidth),(iheight)) }}}

也就是说,明确地将联合和结构作为子聚合。

这甚至被允许吗?我必须期望其他编译器拒绝这个吗?

From what I understand, it's correct in c11 to initialize the fields of an anonymous struct as if they were directly fields of the containing struct?

这有两个部分。首先,我们需要解决这样的成员是否可以初始化的问题,因为 Paragraph 6.7.2.1/13 identifies anonymous structure and union members as specific kinds of "unnamed members", and paragraph 6.7.9/9

Except where explicitly stated otherwise, for the purposes of this subclause unnamed members of objects of structure and union type do not participate in initialization.

第 6.7.9 节(初始化)的其余部分没有说任何我会解释为明确适用于匿名结构和匿名联合成员本身的内容,但我不认为目的是防止初始化匿名成员的 named 成员,特别是考虑到它们被视为包含结构或联合的成员(见下文)。因此,我不会将标准解释为禁止您尝试执行的初始化。

所以是的,我阅读了 C11 以允许您的初始化程序并指定它具有您似乎想要的效果。特别是,标准的 paragraph 6.7.2.1/13 部分表示

The members of an anonymous structure or union are considered to be members of the containing structure or union. This applies recursively if the containing structure or union is also anonymous.

因此,您的初始化程序满足 paragraph 6.7.9/7 中的约束,其中的指示符指定当前对象成员的名称(在您的例子中,是 struct PG_Bounds)。第 6.7.9 节的以下段落介绍了初始化程序的语义,我认为没有理由将它们解释为指定除使用您提供的值初始化整个对象之外的任何内容。

在这一点上,我重申 gcc 正在发出 警告,而不是拒绝您的代码,在这种情况下,我认为警告是虚假的。我写了一个测试程序,就像我在评论中建议的那样,并在 C11 模式下在 gcc 4.8.5 上进行了尝试。尽管 gcc 发出了与您提出的相同警告(但仅在 -Wextra 启用的情况下),但我能够证明您的初始化程序已将主题 struct PG_Bounds 的所有成员初始化为预期值。


您还观察到,如果将初始化器更改为使用嵌套大括号括起来的初始化器的版本,gcc 不会发出警告,并询问

Is this even allowed? Do I have to expect other compilers to reject this?

相对于第 6.7.9/9 段,这可能被视为问题更大,因此从这个意义上说,它可能更具风险。我不确定是否有任何编译器实际上拒绝它或对它做了错误的事情。我认为标准的目的是允许这个初始化器,但我更喜欢另一种形式,我自己。