为什么在 gcc 上接受 const 限定变量作为初始值设定项?

问题描述

在最新版本的 gcc(或 clang)中使用 -std=c17 -pedantic-errors -Wall -Wextra

编译此代码
static const int y = 1;
static int x = y;

然后我没有收到编译器诊断消息,即使我相当确定这不是有效的 C 而是约束违规。我们可以通过查看C17 6.7.9/4来证明它是不符合的:

约束
...
具有静态或线程存储持续时间的对象的初始值设定项中的所有表达式应为常量表达式或字符串文字

然后是关于常量表达式的定义,在这个例子中是一个整数常量表达式(6.6):

整数常量表达式应具有整数类型,并且只能具有整数常量、枚举常量、字符常量、结果为整数常量的 sizeof 表达式、_Alignof 表达式和浮点常量的操作数强制转换的直接操作数。

最后是关于整数常量的定义(6.4.4.1/2):

整数常量以数字开头,但没有句点或指数部分。它可能有一个指定其基础的前缀和一个指定其类型的后缀。

因此 const int 变量不是整数常量,也不是整数常量表达式。因此不是有效的初始化程序。这之前已经讨论过(例如 here),我认为已经确定这是不合格的。但是,我的问题是:

为什么 gcc 即使在严格模式下也选择不合规?

clang 显然一直不合规,但 gcc 从 7.3 版的合规变成了 8.0 及以上版本的不合规。即使在没有 -pedantic-errors认模式下,gcc 7.3 及更早版本也会给出“错误:初始化元素不是常量”。

似乎已经对这条消息做出了某种主动的、有意识的决定。为什么在 gcc 中完全删除它,为什么在严格模式 -std=c17 -pedantic-errors 下编译时不保留它?

解决方法

为什么 gcc 即使在严格模式下也选择不合规?

由于所提出的问题是针对开发人员的动机,我们作为第三方必须继续提供的唯一信息来自公共开发工件,例如 GCC bugzilla、存储库提交消息和实际代码。正如评论中指出的那样,该问题在 the Bugzilla comment thread associated with the change 中进行了讨论。

Bugzilla 的讨论似乎表明,开发人员考虑了该领域的标准要求,尽管有些敷衍。请特别查看注释 910。他们提出了语言规范的第 6.6/10 段:

一个实现可以接受其他形式的常量表达式。

他们没有对此进行任何特别的审查,我阅读这些评论更多是为了寻求更改的理由,而不是对 GCC 一致性考虑的深思熟虑。

因此,他们进行更改是因为他们想要实现功能请求,并且他们在标准语言中找到了足够的(对他们而言)理由来考虑更改的行为与语言约束一致,因此不需要诊断.


还有一个隐含的问题,即最近 GCC 对所提供的声明表格的沉默接受是否实际上违反了合规处理者诊断违反约束的义务。

尽管可以将 6.6/10 解释为允许实现接受他们选择的任何表达式,以符合任何类型的常量表达式的要求,但这似乎令人担忧。一段给定的代码是否满足语言的约束不应该依赖于实现。如果接受这些解释点中的任何一个,都可以解决该问题:

  • 6.6/10 应被解释为表达了通用规则的特定情况,即符合要求的实现可以接受不符合要求的代码,但不暗示这样做会使处理器有权将代码视为符合要求。

  • 6.6/10 应该被解释为允许处理器将更多的表达式解释为“常量表达式”,而不是在前面的段落中描述的那些,但这与那些中定义的特定类型的常量表达式的定义无关。段落(“整数常量表达式”和“算术常量表达式”)。

这些并不相互排斥。我赞成后者,as I have written previously,而且我也倾向于前者。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...