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

sudo
  • 5,604
  • 5
  • 40
  • 78
  • 3
    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 `#define`s, or `enum`s, it'll work as you'd expect. – Crowman Feb 06 '14 at 02:27
  • @PaulGriffiths That does fix it, but in a few other cases, I need long long, not int. – sudo Feb 06 '14 at 02:34
  • @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. – sudo Feb 06 '14 at 02:36
  • 2
    @9000: Just put `ULL` at the end of your numbers. – Crowman Feb 06 '14 at 02:41
  • 4
    "for no reason" -- please avoid making such claims; there's a quite valid reason. – Jim Balter Feb 06 '14 at 03:02
  • @JimBalter Sorry, I didn't mean that there really is no reason, just that it appeared to be so when I was trying it. – sudo Feb 06 '14 at 03:47
  • 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.) – Jim Balter Feb 06 '14 at 05:49
  • 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. – Greg A. Woods Feb 27 '14 at 21:20
  • 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. – Keith Thompson Jul 01 '14 at 18:43
  • @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. – sudo May 21 '15 at 22:18
  • @9000: My point is that you can drop the useless casts: `const unsigned long long LATITUDE = (long) 3600000;` – Keith Thompson May 21 '15 at 23:44
  • @KeithThompson Oh, I see what you mean. – sudo May 22 '15 at 01:51
  • @sudo If you're still interested, I've updated my answer (starting with "**ANOTHER UPDATE**"). – Keith Thompson Nov 07 '18 at 21:52

1 Answers1

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

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • 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... – CRD Feb 06 '14 at 08:29
  • 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. – sudo Feb 06 '14 at 23:52
  • @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. – Keith Thompson Feb 07 '14 at 00:09
  • @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. – Keith Thompson Jul 01 '14 at 18:49
  • @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.) – sudo May 21 '15 at 22:22