为什么"初始化元素不是常数"是......不再工作了?

Kam*_*Cuk 5 c gcc language-lawyer constant-expression

static const int a = 42;
static const int b = a;
Run Code Online (Sandbox Code Playgroud)

我希望在这样的代码中出现编译错误.初始值设定项必须是常量表达式或字符串文字.存储在int具有const类型限定符类型的对象中的值不是常量表达式.

我编译-Wall -Wextra -pedantic,甚至与-ansi.然后:

令人惊讶的是,以下内容:

static const char * const a = "a";
static const char * const b = a;
Run Code Online (Sandbox Code Playgroud)

对于下面的剪辑,我认为我100%确定它不应该编译:

static const int a[] = { 1, 2, 3 };
static const int b = a[1];
Run Code Online (Sandbox Code Playgroud)

,但是:

我尝试浏览网络以获得解释,这主要是导致从旧的stackoverflow问题中复制出来的代码,这些问题不是关于不是恒定的初始化器,而是发现它们现在是否正常工作.我找不到任何与gcc 8相关的变化.

我搞不清楚了.这是预期的行为,这样的代码应该编译吗?为什么/为什么不呢?gcc7.4和gcc8.1之间有什么变化?这是编译器错误吗?这是编译器扩展吗?

Joh*_*ger 5

具有静态存储持续时间的对象的初始化程序必须由常量表达式组成。作为@EugenSh。在评论中观察到,“恒定表达”是一个定义的术语。具体来说,在C2011中是6.6节的主题。描述很简单

常量表达式可以在转换过程中而不是在运行时求值,因此可以在常量可能存在的任何位置使用它。

但是魔鬼在细节上。常量表达式的语义细节包含针对常量表达式的特定种类和用途的特定规则。

例如,该表达a是不是一个“整数常量表达式”在任何情况下,无论其类型或const的岬a,因此可以不使用其中该标准要求特定种类的恒定表达,诸如在位域的宽度。

尽管该标准没有给出名称,但它为初始化程序中的常量表达式提供了更为宽松的规则,这就是我们在此处考虑的情况:

该常数表达式应为以下值之一或计算为以下值之一:

  • 算术常数表达式
  • 空指针常量,
  • 地址常数,或
  • 完整对象类型加上或减去整数常量表达式的地址常量。

还定义了术语“算术常数表达式”和“地址常数”:

算术常量表达式应具有算术类型,并且仅应具有整数常量,浮点常量,枚举常量,字符常量,结果为整数常量的sizeof表达式和_Alignof表达式的操作数。[...]

地址常数是一个空指针,一个指向左值的指针,该左值指定一个静态存储持续时间的对象,或者一个指向函数的指针。它应使用一元&运算符或强制转换为指针类型的整数常量显式创建,或通过使用数组或函数类型的表达式隐式创建。[...]

各种b变量的初始化器均不符合这些规则。指定具有const-qualified类型的对象的左值表达式不在标准要求初始化程序的任何常量表达式中允许出现的元素之中。

该标准确实允许

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

,但这并不能覆盖其对初始化程序中出现的常量表达式的特定要求。

每个给定的变量声明都b违反了标准出现在约束之外的“必须”要求。因此,所产生的行为是不确定的,但是该标准不需要诊断。实现可以接受诸如GCC 8.2这样的扩展形式,并且GCC的-pedantic选项仅在标准要求的情况下才能确保诊断,其中不包括这些情况。

由于行为是不确定的,因此各种实现中观察到的行为都不是不合格的。您不能依靠符合标准的实现来拒绝不符合标准的代码。在某些情况下(但不是这些情况),它必须诊断出不符合项,但即使在这种情况下,也无论如何都可以成功翻译。

我迷路了。它是预期的行为,并且应编译此类代码吗?

不,但是不能安全地期望它无法编译。

为什么/为什么不呢?

上面我解释了为什么各种代码不符合要求,因此可能被符合要求的编译器拒绝。但另一方面,不要求合格的编译器拒绝不合格的代码。

在gcc7.4和gcc8.1之间有什么变化?这是编译器错误吗?这是编译器扩展吗?

海湾合作委员会显然实施了一项扩展。我不确定这是否是故意的,但这肯定是结果。只要行为是人们天真的期望,它看起来很自然和良性,只是从GCC(不是帮助您编写符合标准的代码)的角度来看。