没有命名成员的结构在哪里有用?

Posted

技术标签:

【中文标题】没有命名成员的结构在哪里有用?【英文标题】:Where is struct with no named members useful? 【发布时间】:2013-10-12 00:54:18 【问题描述】:

C 标准为什么以及在哪里允许编译此代码?它在哪里有用?

struct foo 
  int : 12;
;

【问题讨论】:

我在想@Carl Norum 的回答很有用(和往常一样)为什么要删除它? 已删除答案下的评论给出了 C11 引用,即这种结构会调用未定义的行为,这在 ouah 的答案中有所揭示。然后给出评论,因为这个原因,答案被删除。 【参考方案1】:

这将在 §6.7.2.1 结构和联合说明符中

12) 没有声明符但只有冒号和宽度的位域声明表示 未命名的位域。126

脚注解释了为什么存在这样的事情:

126 未命名的位域结构成员可用于填充以符合外部强加的布局。

话虽如此,标准的同一部分(第 8 段)还规定:

如果 struct-declaration-list 不包含任何命名成员,无论是直接还是通过匿名结构或匿名联合,则行为未定义。

但一些编译器(至少是 GCC 和 clang)允许这样做,作为扩展。

如果这是结构中唯一的位域,则使用会受到一些限制,但并非不可能像ouah 说明的那样使用。

标准继续着另一个“奇怪”:

作为一种特殊情况,宽度为 0 的位域结构成员表示不再将位域打包到放置前一个位域(如果有)的单元中。

【讨论】:

并且由于标准未定义该行为,因此每个编译器可能会对其进行不同的处理,只是为了增加混淆^H^H^H^H^H^H^H^H^Hfun .【参考方案2】:

此程序调用未定义的行为。

C 说:

(C99, 6.7.2.1p7) "[...] 如果 struct-declaration-list 不包含命名成员,则行为未定义。"

现在一些编译器接受它作为扩展。这有什么用?

例如对于Linux内核著名的BUILD_BUG_ON_ZERO宏:

#define BUILD_BUG_ON_ZERO(e) (sizeof(struct  int:-!!(e); ))

要看这个宏是做什么的,可以看here。

【讨论】:

虽然BUILD_BUG_ON_ZERO 宏使用了一个不包含名称的结构类型,但它看起来并不依赖于它(除了不使用名称比使用更卫生)名称)。他们的关键是一个失败的表达式会为位域大小产生一个负值,这是被诊断出来的。 @Kaz 它实际上使用它。该宏在具有命名成员的 GNU C 中不起作用。 C 不允许命名位域宽度为 0,只允许未命名位域。而且 GNU C 也不允许这样做:gcc 使用宽度为 0 的命名位域停止翻译。 但您当然可以添加另一个具有名称的虚拟结构成员。 @Kaz 不,您不能添加另一个虚拟命名成员。如果e 不为零,则BUILD_BUG_ON_ZERO 必须错误,如果e0,则必须产生0。例如,请参见 Linux ARRAY_SIZE 宏实现,它假定这一点并在 e0 时使用 BUILD_BUG_ON_ZERO 作为标识元素。通过添加一个虚拟命名成员(不能像我们上面看到的那样具有0 宽度),当e0 时,您不能再填充具有BUILD_BUG_ON_ZERO 产生0 的约束。 您可以轻松地弯曲表达式以返回零。 #define BUILD_BUG_ON_ZERO(e) (<standard-conforming hack here>, 0)#define BUILD_BUG_ON_ZERO(e) ((<standard-conforming-hack>) * 0) 等。黑客返回什么并不重要;我们只关心它是否在编译时爆炸。【参考方案3】:

好吧,根据语言规范,如果您的程序包含没有命名成员的结构类型,则行为是未定义的。 (对于为什么它没有被官方认定为违反约束的问题,我没有立即回答。)它在 6.7.2.1/7 中有说明

struct-declaration-list 是一个声明序列 结构或工会的成员。如果结构声明列表 不包含命名成员,行为未定义。

除此之外,这样的声明并不是真正“有用”,因为它产生的唯一东西是未定义的行为。

【讨论】:

据我了解,一些编译器提供了一种语言扩展,允许结构具有匿名成员,并将这些成员的名称导入到封闭结构的命名空间中。由于标准没有指定内部结构成员的命名成员将被视为外部结构成员的命名成员,这是否意味着标准将禁止其成员都是匿名导入结构的结构? @supercat:我不确定我是否理解正确。一些编译器确实允许匿名结构成员(具有您描述的行为),但这是一个非标准扩展。 C 语言不支持匿名结构成员。这就是标准的全部内容。 我刚刚看到你的更新。也许我上面的评论提供了它不是违反约束的原因:编译器可能作为语言扩展为没有命名成员的结构提供有用的含义。没有定义含义但某些编译器可能为其定义了一些有用含义的语言结构最好保留为 UB,而不是违反约束,即使对于任何未定义有用含义的编译器 都是一个好主意意思是发出诊断。 我的观点是,如果标准说某些构造 X 是未定义的行为,那么编译器供应商可以提供完全符合标准的编译器,由供应商指定以某种有用的方式实现 X。相比之下,如果标准说 X 是违反约束的,那么符合标准的编译器将被禁止让该构造做一些有用的事情。 @supercat:嗯,首先,当语言作者预期某些正式禁止的结构的潜在可用性时,他们通常将其保留为“实现定义的行为”。其次,许多编译器确实提供了大量基于违反约束的代码的扩展。在扩展方面,实现完全可以做任何事情,只要它不改变任何符合的行为。

以上是关于没有命名成员的结构在哪里有用?的主要内容,如果未能解决你的问题,请参考以下文章

c ++结构没有命名类型

在C中参考命名的结构成员直接初始化或分配变量数组的结构成员?

Dirent 结构的成员

匿名结构和联合何时在 C11 中有用?

C99 语言中具有未命名成员的结构的正确行为是啥?

10. 结构和枚举