Ошибка "Элемент инициализатора не является постоянной" без причины в Linux GCC, компиляция C
Я беру свой файл main.c и компилирую его с помощью gcc -std=c1x -c main.c в Mac OS X, и он работает без ошибок. Затем я делаю то же самое в 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)?
1 ответ
Язык C требует, чтобы инициализатор для статического объекта был постоянным выражением. (Так как инициализация статических объектов происходит раньше main
начинается, нет места для какой-либо оценки во время выполнения.)
C о const
Ключевое слово не означает "постоянный", хотя слова, очевидно, связаны между собой. Константное выражение - это выражение, которое может и, в некоторых случаях, должно оцениваться во время компиляции. const
означает только для чтения. Например, в области видимости блока (внутри определения функции) это:
const int r = rand();
совершенно законно. Очевидно, инициализатор не может быть оценен во время компиляции; const
просто означает, что r
не может быть изменено после его инициализации.
Когда вы пишете:
const unsigned long long LATITUDE = (long) 3600000;
ссылка на LATITUDE
не является постоянным выражением. Компилятор, безусловно, может оценить такую ссылку во время компиляции, но стандарт Си не требует этого. (Границу между константными и непостоянными выражениями нужно было где-то провести, и авторы языка решили сделать различие относительно простым, за исключением нескольких особых случаев.)
Теперь, безусловно, верно, что язык C мог быть определен так, чтобы LATITUDE
это постоянное выражение. Это на C++, и я утверждал, что C принимает аналогичное правило. Но в соответствии с действующими правилами C это не так, что означает, что вы не можете использовать LATITUDE
в инициализаторе для статического объекта.
Это также означает, что clang
(компилятор, который, как я понимаю, вызывается при вводе gcc
в MacOS), скорее всего, не соответствует требованиям, поскольку не может диагностировать эту ошибку. В моей собственной системе Linux я нахожу это, когда вызывается с -std=c11 -pedantic
, gcc 4.7.2 правильно диагностирует ошибку, а clang 3.4 - нет.
За исключением, возможно, этого пункта из раздела 6.6 пункта 10 стандарта ISO C 2011 года (который также существует в стандартах 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;
}
}
с лязгом 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.
С 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)
броски в первых двух декларациях бесполезны. Константы 3600000
а также 1810000
имеют (вероятно) тип int
, Вы конвертируете их в long
а затем использовать результат для инициализации объекта типа unsigned long long
, Просто бросьте приведение - или, если вы хотите быть более явным, добавьте ULL
суффикс, чтобы сделать константы unsigned long long
:
const unsigned long long LATITUDE = 3600000ULL;
const unsigned long long LONGITUDE = 1810000ULL;
Проблема в третьей декларации, которая относится к LATITUDE
а также LONGITUDE
, ни одно из которых не является постоянным выражением. К сожалению, C не обеспечивает хороший способ определить именованные константы целочисленных типов, кроме int
(Вы можете (а) использовать enum
особенность для int
константы). Альтернатива - использовать макросы. Это работает:
#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
в большем выражении.)