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

1

13

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

Exorcist answered 6/2, 2014 at 1:52 Comment(13)
This is just a C thing. Per C11 6.6.6, integer constant expressions must only have operands that are constants, or sizeof and _Alignof expressions, and your first two variables here are not constants. I'm not sure why clang allows this, although 6.6.10 does say an implementation is allowed to accept other forms of constant expressions. The arithmetic is fine - if you replace those first two statements with #defines, or enums, it'll work as you'd expect.Vasyuta
@PaulGriffiths That does fix it, but in a few other cases, I need long long, not int.Exorcist
@ruakh It's 4.8.1 in LinuxMint. When I do that on Mac, it says 4.2.1 but then Apple LLVM 5.0, so I guess it's not really the same GCC.Exorcist
@9000: Just put ULL at the end of your numbers.Vasyuta
"for no reason" -- please avoid making such claims; there's a quite valid reason.Vinosity
@JimBalter Sorry, I didn't mean that there really is no reason, just that it appeared to be so when I was trying it.Exorcist
Your claim makes an unwarranted assumption that is contrary to what any knowledgeable/experienced C programmer is aware of. It's best to just ask the question, and leave the answering to the experts ... making your own assumptions can make it less likely to get a valid or timely answer. (See XY Problem.)Vinosity
I agree with @Keith's answer, but I don't think it helps you solve what you're trying to do. :-) What you should do is define any constant integer values as anonymous enums.Mustard
It's odd that you define LATITUDE and LONGITUDE as long long, but initialize them with casts to long. If you drop the cast, an integer constant like 3600000 is of some time in which its value can be represented (unless it exceeds LLONG_MAX). Or you can write 3600000LL if you want to be more explicit.Kaete
@KeithThompson I did that because I was using them in other calculations with long long values, and I didn't want to make the program have to convert them every time.Exorcist
@9000: My point is that you can drop the useless casts: const unsigned long long LATITUDE = (long) 3600000;Kaete
@KeithThompson Oh, I see what you mean.Exorcist
@Exorcist If you're still interested, I've updated my answer (starting with "ANOTHER UPDATE").Kaete
K
25

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

Kaete answered 6/2, 2014 at 8:14 Comment(5)
Mea culpa, the LHS is not a constant it is "read only". Maybe the reason for not defining const to produce a constant is because of const volatile - which is valid...Lumberman
When I compile it with clang, it also runs properly, so it's more than just not noticing the error – there's something non-standard it's doing. Thanks for clearing this up because I always (that is, for a month while I was learning) assumed based on how my compiler behaved that I was properly defining these things as constant.Exorcist
@9000: See my update (last 3 paragraphs). I still think this is a bug in clang, but the C standard might provide an excuse for it.Kaete
@9000: It's a clang extension (apparently based on a GNU extension). It's still a bug that it doesn't warn about the initialization in your code sample. I've updated my answer.Kaete
@KeithThompson Thanks, that explains it thoroughly. I read your update a long time ago but forgot to reply. (I originally asked this question while stuck with this problem on a high school CS project.)Exorcist

© 2022 - 2024 — McMap. All rights reserved.