Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

static struct initialization in c99

I have encountered a strange behaviour when using compound literals for static struct initialization in GCC in c99/gnu99 modes.

Apparently this is fine:

struct Test
{
    int a;
};

static struct Test tt = {1}; /* 1 */

However, this is not:

static struct Test tt = (struct Test) {1}; /* 2 */

This triggers following error:

initializer element is not constant

Also this does not help either:

static struct Test tt = (const struct Test) {1}; /* 3 */

I do understand that initializer value for a static struct should be a compile-time constant. But I do not understand why this simplest initializer expression is not considered constant anymore? Is this defined by the standard?

The reason I'm asking is that I have encountered some legacy code written in GCC in gnu90 mode, that used such compound literal construct for static struct initialization (2). Apparently this was a GNU extension at the time, which was later adopted by C99.

And now it results in that the code that successfully compiled with GNU90 cannot be compiled with neither C99, nor even GNU99.

Why would they do this to me?

like image 893
Ganil Avatar asked Aug 06 '15 18:08

Ganil


People also ask

Can you make a struct static?

A structure declaration cannot be declared static, but its instancies can. You cannot have static members inside a structure because the members of a structure inherist the storage class of the containing struct. So if a structure is declared to be static all members are static even included substructures.

How do you initialize a struct value?

An initializer for a structure is a brace-enclosed comma-separated list of values, and for a union, a brace-enclosed single value. The initializer is preceded by an equal sign ( = ).

Do structs need to be initialized?

The members must be initialized in the same order in which they are declared in the struct, otherwise an error will result. Members not designated an initializer will be value initialized.

What are the rules for initializing structures in C?

Like other C variable types, structures can be initialized when they're declared. This procedure is similar to that for initializing arrays. The structure declaration is followed by an equal sign and a list of initialization values is separated by commas and enclosed in braces.


2 Answers

This is/was a gcc bug (HT to cremno), the bug report says:

I believe we should just allow initializing objects with static storage duration with compound literals even in gnu99/gnu11. [...] (But warn with -pedantic.)

We can see from the gcc document on compound literals that initialization of objects with static storage duration should be supported as an extension:

As a GNU extension, GCC allows initialization of objects with static storage duration by compound literals (which is not possible in ISO C99, because the initializer is not a constant).

This is fixed in gcc 5.2. So, in gcc 5.2 you will only get this warning when using the -pedantic flag see it live, which does not complain without -pedantic.

Using -pedantic means that gcc should provide diagnostics as the standard requires:

to obtain all the diagnostics required by the standard, you should also specify -pedantic (or -pedantic-errors if you want them to be errors rather than warnings)

A compound literal is not a constant expression as covered by the C99 draft standard section 6.6 Constant expressions, we see from section 6.7.8 Initialization that:

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

gcc is allowed to accept other forms of constant expressions as an extension, from section 6.6:

An implementation may accept other forms of constant expressions.

interesting to note that clang does not complain about this using -pedantic

like image 119
Shafik Yaghmour Avatar answered Sep 22 '22 21:09

Shafik Yaghmour


C language relies on an exact definition of what is constant expression. Just because something looks "known at compile time" does not mean that it satisfies the formal definition of constant expression.

C language does not define the constant expressions of non-scalar types. It allows implementations to introduce their own kinds of constant expressions, but the one defined by the standard are restricted to scalar types only.

In other words, C language does not define the concept of constant expression for your type struct Test. Any value of struct Test is not a constant. Your compound literal (struct Test) {1} is not a constant (and is not a string literal) and, for this reason, it cannot be used as an initializer for objects with static storage duration. Adding a const qualifier to it will not change anything since in C const qualifier has no relation whatsoever to the concept of constant expression. It will never make any difference in such contexts.

Note that your first variant does not involve a compound literal at all. It uses a raw { ... } initializer syntax with constant expressions inside. This is explicitly allowed for objects with static storage duration.

So, in the most restrictive sense, the initialization with a compound literal is illegal, while the initialization with ordinary { ... } initializer is fine. Some compilers might accept compound literal initialization as an extension. (By extending the concept of constant expression or by taking some other extension path. Consult compiler documentation to figure out why it compiles.)

like image 31
AnT Avatar answered Sep 25 '22 21:09

AnT