"初始化器元素不是常量"错误在Linux GCC中没有任何理由,编译C

sud*_*udo 13 c linux macos gcc

我把我的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
Run Code Online (Sandbox Code Playgroud)

它应该让我做算术,对吗?我可以用实际数字替换它,它会起作用,但随后会变得混乱.无论如何,它在我的Mac上运行良好.在GCC中是否有一些选项我必须在Linux上指定(除了-std = c1x,你在Mac上也不需要)?

Kei*_*son 24

C语言要求静态对象的初始化程序为常量表达式.(由于静态对象的初始化在main开始之前发生,因此没有任何地方可以进行任何运行时评估.)

C的const关键词并不意味着"常数",尽管这些词语显然是相关的.甲常量表达式是一个,可以是,在某些情况下,必须在编译时,评价.const意味着只读.例如,在块范围内(在函数定义内),这:

const int r = rand();
Run Code Online (Sandbox Code Playgroud)

是完全合法的.显然,初始化程序无法在编译时进行评估; 在const仅仅意味着r它已经initalized后,不得修改.

当你写:

const unsigned long long LATITUDE = (long) 3600000;
Run Code Online (Sandbox Code Playgroud)

引用LATITUDE不是常量表达式.编译器当然可以在编译时评估这样的引用,但C标准并不要求它.(常量和非常量表达式之间的界限必须在某处绘制,并且该语言的作者选择使区分相对简单,几乎没有特殊情况.)

现在确实可以定义C语言,这LATITUDE是一个常量表达式.它是用C++编写的,我认为C采用了类似的规则.但是根据当前的C规则,它不是,这意味着你不能LATITUDE在初始化器中使用静态对象.

这也意味着clang(据我所知,编译器是gcc在MacOS下键入时调用的编译器)非常可能不符合,因为它无法诊断此错误.在我自己的Linux系统上,我发现,当调用时-std=c11 -pedantic,gcc 4.7.2正确诊断错误,但是clang 3.4没有.

除了也许从第6.6节,2011年ISO C标准的第10段(其也存在于1990和1999的标准)这个子句:

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

可以想象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;
    }
}
Run Code Online (Sandbox Code Playgroud)

有了clang 3.0和选项-std=c99 -pedantic,我得到:

c.c:7:14: warning: expression is not integer constant expression (but is allowed as an extension) [-pedantic]
        case LATITUDE:
             ^~~~~~~~
1 warning generated.
Run Code Online (Sandbox Code Playgroud)

使用clang 3.4,警告是:

c.c: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.
Run Code Online (Sandbox Code Playgroud)

所以clang确实认识到它不是一个恒定的表达; 错误是它没有警告声明MAX_COORDINATES_NUMBER.

另一个更新:

问题中的代码是:

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);
Run Code Online (Sandbox Code Playgroud)

(long)前两个声明中的强制转换无效.常量36000001810000(可能)类型int.您将它们转换为long然后使用结果初始化类型的对象unsigned long long.只需删除演员 - 或者,如果你想更明确,添加一个ULL后缀来制作常量unsigned long long:

const unsigned long long LATITUDE = 3600000ULL;
const unsigned long long LONGITUDE = 1810000ULL;
Run Code Online (Sandbox Code Playgroud)

问题出在第三个声明中,它引用的是,LATITUDE并且LONGITUDE都不是常量表达式.不幸的是,C不提供定义整数类型的命名常量的好方法int(你可以(ab)将该enum特性用于int常量).另一种方法是使用宏.这有效:

#define LATITUDE 3600000ULL
#define LONGITUDE 1810000ULL
const unsigned long long MAX_COORDINATES_NUMBER = (LATITUDE-1) + LATITUDE*(LONGITUDE-1);
Run Code Online (Sandbox Code Playgroud)

如果你需要MAX_COORDINATES_NUMBER成为一个常量表达式,你也可以使它成为一个宏:

#define LATITUDE 3600000ULL
#define LONGITUDE 1810000ULL
#define MAX_COORDINATES_NUMBER ((LATITUDE-1) + LATITUDE*(LONGITUDE-1))
Run Code Online (Sandbox Code Playgroud)

(当您MAX_COORDINATES_NUMBER在更大的表达式中使用时,需要额外的括号来避免运算符优先级问题.)