Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Initializer element is not constant" error for no reason in Linux GCC, compiling C [duplicate]

Tags:

c

linux

macos

gcc

I take my main.c file and compile it with gcc -std=c1x -c main.c in Mac OS X, and it works fine with no errors. Then I do the exact same thing in LinuxMint and on a Raspberry Pi, and in both cases, it gives me errors about "initializer element is not constant".

One example of a problematic line with relevant code:

//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

It's supposed to let me do arithmetic, right? I could just replace that with the actual numbers, and it would work, but then it would become messy. And it works fine on my Mac anyway. Is there some option in GCC I have to specify on Linux (besides -std=c1x, which you also don't need on Mac)?

like image 302
sudo Avatar asked Feb 06 '14 01:02

sudo


1 Answers

The C language requires the initializer for a static object to be a constant expression. (Since initialization of static objects occurs before main begins, there's no place for any run-time evaluation to happen.)

C's const keyword does not mean "constant", though the words are obviously related. A constant expression is one that can be, and in some cases must be, evaluated at compile time. const means read-only. For example, at block scope (inside a function definition), this:

const int r = rand();

is perfectly legal. Obviously the initializer can't be evaluated at compile time; the const merely means that r may not be modified after it's been initalized.

When you write:

const unsigned long long LATITUDE = (long) 3600000;

a reference to LATITUDE is not a constant expression. A compiler certainly could evaluate such a reference at compile time, but the C standard doesn't require it to. (The line between constant and non-constant expressions had to be drawn somewhere, and the authors of the language chose to make the distinction relatively simple, with few special cases.)

Now it's certainly true that the C language could have been defined so that LATITUDE is a constant expression. It is in C++, and I've argued for C to adopt a similar rule. But under current C rules, it's not, which means that you can't use LATITUDE in the initializer for a static object.

This also means that clang (the compiler that, as I understand it, is the one invoked when you type gcc under MacOS) is very likely non-conforming, because it fails to diagnose this error. On my own Linux system, I find that, when invoked with -std=c11 -pedantic, gcc 4.7.2 correctly diagnoses the error, but clang 3.4 does not.

Except perhaps for this clause from section 6.6 paragraph 10 of the 2011 ISO C standard (which also exists in the 1990 and 1999 standards):

An implementation may accept other forms of constant expressions.

It's conceivable that clang accepts LATITUDE as a constant expression because it takes advantage of this permission -- but then I'd still expect at least a warning from clang -std=c11 -pedantic -Wall -Wextra, and there is none.

UPDATE : When I compile the following:

#include <stdio.h>

const unsigned long long LATITUDE = (long) 3600000;

int main(void) {
    switch (0) {
        case LATITUDE:
            puts("wrong");
            break;
        default:
            puts("ok(?)");
            break;
    }
}

with clang 3.0 with options -std=c99 -pedantic, I get:

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

With clang 3.4, the warning is:

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.

So clang does recognize that it's not a constant expression; the bug is that it doesn't warn about the declaration of MAX_COORDINATES_NUMBER.

ANOTHER UPDATE :

The code in the question is:

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

The (long) casts in the first two declarations are not useful. The constants 3600000 and 1810000 are (probably) of type int. You convert them to long and then use the result to initialize an object of type unsigned long long. Just drop the cast -- or, if you want to be more explicit, add a ULL suffix to make the constants unsigned long long:

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

The problem is on the third declaration, which refers to LATITUDE and LONGITUDE, neither of which is a constant expression. Unfortunately C doesn't provide a good way to define named constants of integer types other than int (you can (ab)use the enum feature for int constants). The alternative is to use macros. This works:

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

And if you need MAX_COORDINATES_NUMBER to be a constant expression, you can make it a macro as well:

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

(The extra parentheses are needed to avoid operator precedence problems when you use MAX_COORDINATES_NUMBER in a larger expression.)

like image 193
Keith Thompson Avatar answered Oct 18 '22 03:10

Keith Thompson