为什么C中的静态初始化表达式不能使用常量数组的元素?

时间:2018-06-27 14:40:01

标签: c arrays initialization constant-expression

以下(公认的)C程序无法编译:

int main() {
const int array[] = {1,2,3};
static int x = array[1];
}

使用gcc(或Microsoft的CL.EXE)编译上述C源文件时,出现以下错误:

error: initializer element is not constant
static int x = array[1];
               ^

这种简单直观的语法肯定有用,因此这似乎应该合法,但显然不是。当然,我不是唯一对这一表面上愚蠢的限制感到沮丧的人。我不明白为什么不允许这样做-C语言试图通过使这种有用的语法非法来避免什么问题?

似乎与编译器生成用于初始化的汇编代码的方式有关,因为如果删除了“ static”关键字(例如,变量“ x”在堆栈上),则它可以编译。

但是,另一个奇怪的事情是,它可以在C ++中很好地编译(即使使用static关键字),但在C中却不能。因此,C ++编译器似乎能够生成执行这种初始化所需的汇编代码。

编辑: 感谢戴维斯洛(Davislor)-为了试图安抚未来的SO权力,我将寻求以下类型的事实信息来回答这个问题:

  1. 是否有任何支持这些语义的遗留代码会破坏?

  2. 是否已经向标准委员会正式提出了这些语义?

  3. 有人有拒绝拒绝这些语义的理由吗?

2 个答案:

答案 0 :(得分:5)

具有静态存储持续时间的对象(读取:在文件作用域或使用static关键字声明的变量)必须通过编译时间常数进行初始化。

C标准中有关初始化状态的第6.7.9节:

  

4 对于具有静态或线程存储持续时间的对象,初始化器中的所有表达式均应为常量表达式或   字符串文字。

关于常量表达式的6.6节规定:

  

7 初始值设定项中的常量表达式允许更大的自由度。这样一个常数   表达式应为以下值之一或计算结果:

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

8 算术常数表达式应具有算术类型,并且仅应具有整数常量的操作数(浮点型)   常数,枚举常数,字符常数,sizeof   结果为整数常量和_Alignof的表达式   表达式。算术常数表达式中的强制转换运算符应   仅将算术类型转换为算术类型,除非作为   大小为或的操作数   _Alignof运算符。

     

9 地址常数是一个空指针,一个指向左值的指针,该左值指定一个静态存储持续时间的对象,或者一个指向a的指针。   功能指示符;它应使用一元&显式创建   运算符或强制转换为指针类型的整数常量,或由   使用数组或函数类型的表达式。的   array-subscript []和member-access。和->运算符,地址&   和间接*一元运算符,以及指针强制转换可用于   地址常量的创建,但对象的值应   不能通过使用这些运算符进行访问。

根据以上定义,const变量不适合作为常量表达式,因此不能用于初始化static对象。另一方面,C ++将const变量视为真实常量,因此允许它们初始化静态对象。

答案 1 :(得分:1)

如果C标准允许这样做,则编译器将必须知道数组中的内容。也就是说,编译器必须具有数组内容的编译时模型。否则,编译器将为每个数组完成少量工作:它需要知道其名称和类型(包括其大小),以及一些其他细节,例如其链接和存储持续时间。但是,在代码中指定了数组初始化的位置,编译器只需将相关信息写入正在增长的目标文件中,然后就可以忽略它。

如果编译器必须能够在编译时从数组中获取值,则它必须记住该数据。由于数组可能非常大,这给委员会可能不希望的C编译器增加了负担,因为C打算在各种各样的环境中运行,包括那些资源有限的环境。

C ++委员会做出了一个不同的决定,而C ++的翻译负担更大。