6

GCC 4.9 and 5.1 reject this simple C99 declaration at global scope. Clang accepts it.

const int a = 1, b = a; // error: initializer element is not constant

How could such a basic feature be missing? It seems very straightforward.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • @ElliottFrisch What version? I tried [4.9 on ideone.com](http://ideone.com/kkET3I) and 5.1 on my local machine. – Potatoswatter May 11 '15 at 01:23
  • 1
    Local 4.9.2 under Debian Jessie rejects it as well. – paxdiablo May 11 '15 at 01:28
  • My guess is that clang accept it because it takes advantage of the permission in [N1570](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf) 6.6 paragraph 10: "An implementation may accept other forms of constant expressions." Either that, or it's a bug in clang. – Keith Thompson May 11 '15 at 01:35
  • @KeithThompson Can you say that in an answer? And if you don't mind copy-pasting the last part of my last comment under Chris' answer… – Potatoswatter May 11 '15 at 01:37
  • 1
    @Potatoswatter: It doesn't really answer the question. You asked why gcc rejects it; why clang accepts it is a different question (and I'm only guessing). – Keith Thompson May 11 '15 at 01:38
  • probably duplicate, http://stackoverflow.com/questions/3025050/error-initializer-element-is-not-constant-when-trying-to-initialize-variable-w – M.M May 11 '15 at 11:09
  • @MattMcNabb Nah, aggregates are in a different league. – Potatoswatter May 11 '15 at 11:14
  • @Potatoswatter 3 ints isn't much different to 1 int – M.M May 11 '15 at 11:18

3 Answers3

9

C991 section 6.6 Constant expressions is the controlling section. It states in subsections 6 and 7:

6/ An integer constant expression shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, and floating constants that are the immediate operands of casts.

Cast operators in an integer constant expression shall only convert arithmetic types to integer types, except as part of an operand to the sizeof operator.

The definition of integer and floating point constants is specified in 6.4.4 of the standard, and it's restricted to actual values (literals) rather than variables.

7/ More latitude is permitted for constant expressions in initializers. Such a constant expression shall be, or evaluate to, one of the following (a) an arithmetic constant expression, (b) a null pointer constant, (c) an address constant, or (d) an address constant for an object type plus or minus an integer constant expression.

Since a is none of those things in either subsection 6 or 7, it is not considered a constant expression as per the standard.

The real question, therefore, is not why gcc rejects it but why clang accepts it, and that appears to be buried in subsection 10 of that same section:

10/ An implementation may accept other forms of constant expressions.

In other words, the standard states what an implementation must allow for constant expressions but doesn't limit implementations to allowing only that.


1 C11 is much the same other than minor things like allowing _Alignof as well as sizeof.

Community
  • 1
  • 1
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
3

This is just the rules of C. It has always been that way. At file scope, initializers must be constant expressions. The definition of a constant expression does not include variables declared with const qualifier.

The rationale behind requiring initializers computable at compile-time was so that the compiler could just put all of the initialized static data as a bloc in the executable file, and then at load time that bloc is loaded into memory as a whole and voila, the global variables all have their correct initial values without any code needing to be executed.

In fact if you could have executable code as initializer for global variables, it introduces quite a lot of complication regarding which order that code should be run in. (This is still a problem in modern C++).

In K&R C, there was no const. They could have had a rule that if a global variable is initialized by a constant expression, then that variable also counts as a constant expression. And when const was added in C89, they could have also added a rule that const int a = 5; leads to a constant expression.

However they didn't. I don't know why sure, but it seems likely that it has to do with keeping the language simple. Consider this:

extern const int a, b = a;

with const int a = 5; being in another unit. Whether or not you want to allow this, it is considerably more complication for the compiler, and some more arbitrary decisions.

If you look at the current C++ rules for constant expressions (which still are not settled to everyone's satisfaction!) you'll see that each time you add support for one more "obvious" thing then there are two other "obvious" things that are next in line and it is never-ending.

In the early days of C, in the 1970s, keeping the compiler simple was important so it may have been that making the compiler support this meant the compiler used too many system resources, or something. (Hopefully a coder from that era can step in and comment more on this!)

Finally, the C89 standardization was quite a contentious process since there were so many different C compilers that had each gone their own way with language evolution. Demanding that a compiler vendor who doesn't support this, change their compiler to support it might be met with opposition, lowering the uptake of the standard.

Community
  • 1
  • 1
M.M
  • 138,810
  • 21
  • 208
  • 365
0

Because const doesn't make a constant expression -- it makes a variable that can't be assigned to (only initialized). You need constexpr to make a constant expression, which is only available in C++. C99 has no way of making a named constant expression (other than a macro, which is sort-of, but not really an expression at all).

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • Not only does `const` make a constant expression, `constexpr` is C++, not C. – Potatoswatter May 11 '15 at 01:29
  • No, `const` does not make a constant expression. See 6.7.3 of the spec. The fact that it doesn't make a constant expression is why C++ introduced `constexpr` – Chris Dodd May 11 '15 at 01:33
  • I don't see anything there one way or another. More relevant is §6.6, but it just seems to make circular logic that constant expressions are constant. If it wanted to restrict constant grammatical terminals to literals, it should say literals. … Aha, "constant" actually does mean "literal," per §6.4.4.1. – Potatoswatter May 11 '15 at 01:36
  • 3
    C does let you define named constant expressions of type `int`: `enum { answer = 42 };` – Keith Thompson May 11 '15 at 01:37
  • 1
    @ChrisDodd after `const int a = 5;`, `a` is a constant expression in C++ but not C – M.M May 11 '15 at 01:39
  • @Potatoswatter: In both C and C++, given `const int r = rand();`, `r` is not a constant expression (because it can't be). `const` really means read-only. C++ merely adds a special case rule that makes the names of *some* const-qualified objects constant (evaluated at compile time). `const` and "constant" are obviously closely related words, but they have quite different meanings in both C and C++. – Keith Thompson May 11 '15 at 17:44
  • @KeithThompson Yes, `rand()` initializes a variable to be a C++98 non-constant expression but `5` initializes it as a constant. C++ is off-topic to this question. – Potatoswatter May 12 '15 at 01:40