问题
static const int a = 42;
static const int b = a;
I would expect a compilation error in such code. The initializer must be a constant expression or a string literal. A value stored in an object with the type int
with const
type qualifier is not a constant expression.
I compile with -Wall -Wextra -pedantic
, even with -ansi
. Then:
- the code compiles fine on gcc8.2 and gcc 8.1
- the code fails to compile on gcc lower then 7.4 with
error: initializer element is not constant
- the code compiles fine on all clang versions
Surprisingly, the following:
static const char * const a = "a";
static const char * const b = a;
- works on gcc 8.2
- fails on gcc below 7.4 with
error: initializer element is not constant
- works on clang greater then 4.0
- fails on clang lower then 3.9.1 with
error: initializer element is not a compile-time constant
For the snipped below, I think I was 100% sure that it should not compile:
static const int a[] = { 1, 2, 3 };
static const int b = a[1];
, but:
- it works on gcc 8.2
- it fails on gcc lower then 7.4 with
error: initializer element is not constant
- and it fails on any version of clang
I tried browsing the net for an explanation, which mostly resulted in copying the code out of old stackoverflow questions about not constant intializers and finding out if they work now. I couldn't find anything relevant in gcc 8 changes.
I am lost. Is it expected behavior and such code should compile? Why/Why not? What changed between gcc7.4 and gcc8.1? Is this a compiler bug? Is this a compiler extension?
回答1:
Initializers for objects with static storage duration are required to be composed of constant expressions. As @EugenSh. observed in comments, "constant expression" is a defined term. Specifically, in C2011 it is the subject of section 6.6. The description is simply
A constant expression can be evaluated during translation rather than runtime, and accordingly may be used in any place that a constant may be.
But the devil is in the details. The semantic details of constant expressions contain specific rules for particular kinds and uses of constant expressions.
For example, the expression a
is not an "integer constant expression" under any circumstance, regardless of the type or const
ness of a
, and therefore may not be used where the standard requires that specific kind of constant expression, such as in bitfield widths.
Although the standard does not give a name to it, it presents slightly more relaxed rules for constant expressions in initializers, which is the case we're considering here:
Such a constant expression shall be, or evaluate to, one of the following:
- an arithmetic constant expression,
- a null pointer constant,
- an address constant, or
- an address constant for a complete object type plus or minus an integer constant expression.
The terms "arithmetic constant expression" and "address constant" are also defined:
An arithmetic constant expression shall have arithmetic type and shall only have operands that are integer constants, floating constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, and
_Alignof
expressions. [...]An address constant is a null pointer, a pointer to an lvalue designating an object of static storage duration, or a pointer to a function designator; it shall be created explicitly using the unary & operator or an integer constant cast to pointer type, or implicitly by the use of an expression of array or function type. [...]
None of the initializers for your various b
variables conform to those rules. An lvalue expression designating an object having const
-qualified type is not among the elements that are permitted to appear in any of the varieties of constant expression that the standard requires for initializers.
The standard does allow generally that
An implementation may accept other forms of constant expressions.
, but that does not override its specific requirements for constant expressions appearing in initializers.
Each of the given declarations of a variable b
violates a "shall" requirement of the standard appearing outside a constraint. The resulting behavior is therefore undefined, but the standard does not require a diagnostic. Implementations may accept such forms as an extension, as GCC 8.2 evidently does, and GCC's -pedantic
option ensures diagnostics only where required by the standard, which does not include these cases.
Since the behavior is undefined, none of the observed behavior of various implementations is non-conforming. You cannot rely on a conforming implementation to reject non-conforming code. Under some circumstances (but not these) it must diagnose non-conformance, but even in such cases it is permitted to translate successfully anyway.
I am lost. Is it expected behavior and such code should compile?
No, but neither is it safe to expect that it should fail to compile.
Why/Why not?
I explain above why the various codes fail to conform, and therefore may by rejected by conforming compilers. But on the other side, conforming compilers are not required to reject non-conforming code.
What changed between gcc7.4 and gcc8.1? Is this a compiler bug? Is this a compiler extension?
GCC evidently implemented an extension. I'm uncertain whether that was intentional, but it's certainly the result. As long as the behavior is what one would naively expect, it seems pretty natural and benign, except from the point of view of GCC (not) helping you to write conforming code.
来源:https://stackoverflow.com/questions/54135942/why-initializer-element-is-not-a-constant-is-not-working-anymore