以下(公认的)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权力,我将寻求以下类型的事实信息来回答这个问题:
是否有任何支持这些语义的遗留代码会破坏?
是否已经向标准委员会正式提出了这些语义?
有人有拒绝拒绝这些语义的理由吗?
答案 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 ++的翻译负担更大。