Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
656 views
in Technique[技术] by (71.8m points)

c - Why "initializer element is not a constant" is... not working anymore?

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:

Surprisingly, the following:

static const char * const a = "a";
static const char * const b = a;

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:

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...