在Linux GCC中没有理由“初始化元素不是常量”错误,编译C

我把我的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的声明。