我把我的main.c文件在Mac OS X中用gcc -std = c1x -c main.c进行编译,并且没有任何错误。 然后我在LinuxMint和Raspberry Pi上做了完全相同的事情,在这两种情况下,它给了我关于“初始化器元素不是常量”的错误。
有关代码的问题行的一个例子:
//STATIC GLOBAL CONSTANTS const unsigned long long LATITUDE = (long) 3600000; const unsigned long long LONGITUDE = (long) 1810000; const unsigned long long MAX_COORDINATES_NUMBER = (LATITUDE-1) + LATITUDE*(LONGITUDE-1); //compiler error: initializer element is not constant
它应该让我做算术,对吧? 我可以用实际的数字代替它,它会起作用,但是会变得混乱。 而且,无论如何,它在我的Mac上工作正常。 在GCC中有一些选项,我必须在Linux上指定(除了-std = c1x,你也不需要在Mac上)?
C语言要求静态对象的初始化器是一个常量表达式。 (由于静态对象的初始化发生在main
开始之前,所以没有任何运行时评估发生的地方。)
C的const
关键字并不意味着“常量”,尽管这些词显然是相关的。 常量表达式是可以的,在某些情况下必须在编译时进行评估。 const
表示只读。 例如,在块范围(在一个函数定义中),这个:
const int r = rand();
是完全合法的。 显然,编译时不能评估初始值设定项。 const
仅仅意味着r
在初始化之后不能被修改。
当你写:
const unsigned long long LATITUDE = (long) 3600000;
对LATITUDE
的引用不是一个常量表达式。 编译器当然可以在编译时评估这样的引用,但C标准并不要求它。 (常数表达式和非常数表达式之间的界限必须在某个地方画出来,而这个语言的作者则选择使得这个界限相对简单,很少有特殊情况。)
现在确实可以定义C语言,使得LATITUDE
是一个常量表达式。 它是用C ++编写的,我曾经争辩过C采用类似的规则。 但在目前的C规则下,这不是,这意味着你不能在初始化器中为一个静态对象使用LATITUDE
。
这也意味着clang
(据我所知,编译器是在MacOS下键入gcc
时调用的编译器)很可能是不符合的,因为它无法诊断这个错误。 在我自己的Linux系统上,我发现当用-std=c11 -pedantic
调用时,gcc 4.7.2正确地诊断错误,但是叮当3.4不正确。
除了2011年ISO C标准(也存在于1990年和1999年的标准)第6.6节第10段中的这一条款:
一个实现可能接受其他形式的常量表达式。
可以想象,clang接受LATITUDE
作为一个常数表达式,因为它利用了这个权限 – 但是我仍然期望至少从clang -std=c11 -pedantic -Wall -Wextra
发出警告,而且没有。
更新 :当我编译以下内容:
#include <stdio.h> const unsigned long long LATITUDE = (long) 3600000; int main(void) { switch (0) { case LATITUDE: puts("wrong"); break; default: puts("ok(?)"); break; } }
与铿锵3.0与选项-std=c99 -pedantic
,我得到:
cc:7:14: warning: expression is not integer constant expression (but is allowed as an extension) [-pedantic] case LATITUDE: ^~~~~~~~ 1 warning generated.
3.4时刻,警告是:
cc:7:14: warning: expression is not an integer constant expression; folding it to a constant is a GNU extension [-Wgnu-folding-constant] case LATITUDE: ^~~~~~~~ 1 warning generated.
所以铿锵也承认这不是一个常量表达, 该错误是它不警告关于MAX_COORDINATES_NUMBER
的声明。