【问题标题】:Initialization of anonymous struct, workaround for gcc 4.9匿名结构的初始化,gcc 4.9 的解决方法
【发布时间】:2017-05-26 13:46:44
【问题描述】:

我有以下结构类型:

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,这会给出以下警告:

警告:'struct ' [-Wmissing-field-initializers] 的字段'size' 缺少初始化程序

如果我将初始化程序更改为这个版本,它会起作用:

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

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

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

【问题讨论】:

  • 尝试用-pedantic编译。如果它没有给你警告,你应该很高兴。
  • 是的,我已经这样做了,clang 3.5.0 和 gcc 6.3.0 没有什么可抱怨的。阅读this answer的标准引号后仍然不确定是否允许
  • 编译器并没有拒绝它——它只是发出了一个警告。你应该认真对待这一点,但这可能不是问题。编写一个简短的测试程序来检查对象是否按预期初始化怎么样?
  • @JohnBollinger 好主意,也许 gcc 4.9 实际上“正确的事情”......我会尝试。
  • FWIW,在 C11 模式下编译时,gcc 4.8.5 仅在启用 -Wextra 时才会发出该警告。

标签: c11 c gcc language-lawyer c11


【解决方案1】:

据我了解,在 c11 中初始化匿名结构的字段就好像它们是包含结构的直接字段一样正确?

这有两个部分。首先,我们需要解决这样的成员是否可以初始化的问题,因为Paragraph 6.7.2.1/13 将匿名结构和联合成员标识为特定类型的“未命名成员”,而paragraph 6.7.9/9 表示

除非另有明确说明,就本子条款而言,结构和联合类型对象的未命名成员不参与初始化。

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

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

匿名结构或联合的成员被视为包含结构或联合的成员。如果包含结构或联合也是匿名的,这将递归地应用。

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

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


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

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

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

【讨论】:

  • 感谢您尝试并通过您对标准的解释来确认我的假设。我稍后也会尝试自己,并且会坚持初始化而不嵌套。关于你添加的标签,好吧,这段代码是有实际用途的(它用于一个小的 GUI 库,我希望客户端代码使用这些嵌套的匿名结构更具可读性)——但可能仍然符合语言律师的条件:)
  • 我确认 John 的观点,显然标准的意图是让匿名 struct 的字段像直接指定的那样起作用。 -Wextra 经常有虚假警告,特别是关于 struct 字段的非初始化。 struct 成员的默认初始化是 C 标准的一个重要特性,对此发出警告只会适得其反。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-28
  • 2020-10-22
  • 2011-07-01
  • 2018-01-20
相关资源
最近更新 更多