在Linux GCC中无缘无故地"Initializer element is not constant"错误,编译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 正确诊断错误,但 clang 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;
}
}

使用带有选项-std=c99 -pedantic的clang 3.0,我得到:

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

对于 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.

所以 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);

前两个声明中的(long)转换没有用。常量36000001810000(可能)属于int类型。将它们转换为long然后使用结果初始化类型为unsigned long long的对象。只需删除强制转换 - 或者,如果您想更明确,请添加一个ULL后缀以使常量unsigned long long

const unsigned long long LATITUDE = 3600000ULL;
const unsigned long long LONGITUDE = 1810000ULL;

问题出在第三个声明上,它指的是LATITUDELONGITUDE,两者都不是常量表达式。不幸的是,C 没有提供定义除int以外的整数类型的命名常量的好方法(您可以 (ab) 对int常量使用enum功能)。另一种方法是使用宏。这有效:

#define LATITUDE 3600000ULL
#define LONGITUDE 1810000ULL
const unsigned long long MAX_COORDINATES_NUMBER = (LATITUDE-1) + LATITUDE*(LONGITUDE-1);

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

#define LATITUDE 3600000ULL
#define LONGITUDE 1810000ULL
#define MAX_COORDINATES_NUMBER ((LATITUDE-1) + LATITUDE*(LONGITUDE-1))

(在较大的表达式中使用MAX_COORDINATES_NUMBER时,需要额外的括号以避免运算符优先级问题。

相关内容

最新更新