为什么“初始化程序元素不是常量”不再起作用?

时间:2019-01-10 19:47:26

标签: c gcc language-lawyer constant-expression

static const int a = 42;
static const int b = a;

我期望此类代码中出现编译错误。初始化程序必须是常量表达式或字符串文字。存储在类型为int且类型为const的对象中的值不是常量表达式。

即使使用-Wall -Wextra -pedantic,我也可以使用-ansi进行编译。然后:

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

static const char * const a = "a";
static const char * const b = a;
  • 适用于gcc 8.2
  • 使用error: initializer element is not constant的{​​{3}}失败
  • 适用于gcc below 7.4
  • 使用error: initializer element is not a compile-time constant的{​​{3}}失败

对于下面的片段,我认为我100%确信它不应该编译:

static const int a[] = { 1, 2, 3 };
static const int b = a[1];

,但是:

我尝试浏览网络进行解释,这主要是导致将代码从旧的stackoverflow问题中复制出来,这些问题涉及不固定的初始化器,并找出它们现在是否可以正常工作。我在gcc lower then 7.4中找不到任何相关内容。

我迷路了。它是预期的行为,并且应编译此类代码吗?为什么/为什么不呢?在gcc7.4和gcc8.1之间有什么变化?这是编译器错误吗?这是编译器扩展吗?

1 个答案:

答案 0 :(得分:4)

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

  

常量表达式可以在翻译过程中求值,而不是   运行时,因此可以在任何常量可以使用的地方使用   是。

但细节在于魔鬼。常量表达式的语义细节包含针对特定类型和常量表达式使用的特定规则。

例如,无论a的类型或const的性质如何,表达式a在任何情况下都不是“整数常量表达式”,因此在以下情况下可能无法使用该标准要求使用特定类型的常量表达式,例如位域宽度。

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

  

这样的常数表达式应为或计算为   以下:

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

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

  

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

     

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

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

该标准通常允许

  

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

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

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

由于未定义行为,因此在各种实现中观察到的行为均未符合标准。您不能依靠符合标准的实现来拒绝不符合标准的代码。在某些情况下(但不是在这些情况下),它必须诊断不符合项,但即使在这种情况下,也仍然允许其成功翻译。

  

我迷路了。这是预期的行为,应该编译这样的代码吗?

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

  

为什么/为什么不呢?

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

  

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

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