ccompiler-errorsvariable-initialization

Why is this static const char* initialization architecture-specific?


This question relates to Why is constant initialization need for static char* but not static char**

An answer to the noted question taught me that static const char* initializations must be to an address resolvable at compile-time. The same answer pointed out that the pointer needed to be const, and not (necessarily) the content where the pointer pointed. This is reflected in the code below.

Why, then, is the following code target architecture-dependent? (i.e. why does this code generate a compiler error with some but not all cross-compilers?)

// main.c

static const char* const text = "foo";
static const char* tmp = text;

int main( int argc, char* argv[] ) { return 0; }

Successful compilation:

$ /path/to/tgt1-linux-gnu-gcc --version && /path/to/tgt1-linux-gnu-gcc ./main.c && echo $?
tgt1-linux-gnu-gcc (crosstool-NG 1.23.0.485-ee829 - next) 8.2.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

0

Unsuccessful compilation:

$ /path/to/tgt2-linux-gnueabi-gcc --version && /path/to/tgt2-linux-gnueabi-gcc ./main.c
tgt2-linux-gnueabi-gcc (crosstool-NG 1.18.0) 4.7.3 20130102 (prerelease)
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

./main.c:4:1: error: initializer element is not constant

Solution

  • The code is not architecture-dependent, but it does fail to conform to any version of the C language standard. Compilers have no obligation to handle non-conforming code in a consistent way. They are not even required to reject it, and depending on the details, they may not even be required to say anything about it at all.

    As @JL2210 has already observed, you are, in fact, using two different compilers, even though both are versions of GCC. These particular versions conform to quite different language standards by default -- GCC 4 defaults to C89, whereas GCC 8 defaults to C2011 -- but that isn't itself the issue. All versions of the C standard have very similar wording in the applicable areas, and the minor differences don't change how they apply to your code.

    With respect to initializers, C89 contains this language constraint:

    All the expressions in an initializer for an object that has static storage duration [...] shall be constant expressions.

    whereas the analogous constraint in C11 is

    All the expressions in an initializer for an object that has static or thread storage duration shall be constant expressions or string literals.

    Before you get excited about the "string literals" addition in C11, I note that the initialization expression in question, text, is unequivocally not a string literal, notwithstanding how the variable involved is itself initialized. The question thus comes down to whether it is a "constant expression". Regardless of all the const-ness in its type, however, it isn't.

    Constant expression is a defined term in the standard. There are several varieties, but the expression here not being a null pointer constant, the alternative needed here boils down to an address constant or such a constant plus or minus an integer constant expression. There's no integer constant expression involved here, so an address constant itself is the remaining option.

    In C89, this is the relevant definition:

    An address constant is a pointer to an lvalue designating an object of static storage duration, or to a function designator; it shall be created explicitly, using the unary & operator, or implicitly, by the use of an expression of array or function type. The array-subscript [] and member-access . and -> operators, the address & and indirection * unary operators, and pointer casts may be used in the creation an address constant, but the value of an object shall not be accessed by use of these operators.

    C11 puts it this way:

    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. The array-subscript [] and member-access . and -> operators, the address & and indirection * unary operators, and pointer casts may be used in the creation of an address constant, but the value of an object shall not be accessed by use of these operators.

    (Emphasis added in both cases.)

    The expression text is not an explicit expression in the unary & operator. It is not an integer constant cast to pointer type. It is not an expression of array type (nor of function type).

    Any way around, then, not only does your code fail to conform, but it violates a language constraint. A conforming compiler is therefore obligated to emit a diagnostic. GCC is not obligated to reject the code, but that's one conforming alternative. Under the circumstances, it is entirely reasonable that version 8 realizes that the initializer really is constant, despite not technically being a constant expression. In order for it to actually conform with the diagnosis requirement, however, you may need to give it the -pedantic option. Emitting all such required diagnostics is exactly the purpose of that option.