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?
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
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.)
Interestingly, the clang does not complain with this code, even with -pedantic-errors flag.
This is most certainly about C11 §6.7.9/p4 Initialization (emphasis mine going forward)
All the expressions in an initializer for an object that has static or
thread storage duration shall be constant expressions or string
literals.
Another subclause to look into is §6.5.2.5/p5 Compound literals:
The value of the compound literal is that of an unnamed object
initialized by the initializer list. If the compound literal occurs
outside the body of a function, the object has static storage
duration; otherwise, it has automatic storage duration associated with
the enclosing block.
and (for completeness) §6.5.2.5/p4:
In either case, the result is an lvalue.
but this does not mean, that such unnamed object can be treated as constant expression. The §6.6 Constant expressions says inter alia:
2) A constant expression can be evaluated during translation rather
than runtime, and accordingly may be used in any place that a constant
may be.
3) Constant expressions shall not contain assignment, increment,
decrement, function-call, or comma operators, except when they are
contained within a subexpression that is not evaluated.
10) An implementation may accept other forms of constant expressions.
There is no explicit mention about compound literals though, thus I would interpret this, they are invalid as constant expressions in strictly conforming program (thus I'd say, that clang has a bug).
Section J.2 Undefined behavior (informative) also clarifies that:
A constant expression in an initializer is not, or does not 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
(6.6).
Again, no mention about compound literals.
Neverthless, there is a light in the tunnel. Another way, that is fully sanitized is to convey such unnamed object as address constant. The standard states in §6.6/p9 that:
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.
hence you can safely initialize it with constant expression in this form, because such compound literal indeed designates an lvalue of object, that has static storage duration:
#include <stdio.h>
struct Test
{
int a;
};
static struct Test *tt = &((struct Test) {1}); /* 2 */
int main(void)
{
printf("%d\n", tt->a);
return 0;
}
As checked it compiles fine with -std=c99 -pedantic-errors flags on both gcc 5.2.0 and clang 3.6.
Note, that as opposite to C++, in C the const qualifier has no effect on constant expressions.
ISO C99 does support compound literals (according to this). However, currently only the GNU extension provides for initialization of objects with static storage duration by compound literals, but only for C90 and C++.
A compound literal looks like a cast containing an initializer. Its value is an object of the type specified in the cast, containing the elements specified in the initializer; it is an lvalue. As an extension, GCC supports compound literals in C90 mode and in C++, though the semantics are somewhat different in C++.
Usually, the specified type is a structure. Assume that struct foo and structure are declared as shown:
struct foo {int a; char b[2];} structure;
Here is an example of constructing a struct foo with a compound literal:
structure = ((struct foo) {x + y, 'a', 0});
This is equivalent to writing the following:
{
struct foo temp = {x + y, 'a', 0};
structure = temp;
}
GCC 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 ). It is handled as if the object is initialized only with the bracket enclosed list if the types of the compound literal and the object match. The initializer list of the compound literal must be constant. If the object being initialized has array type of unknown size, the size is determined by compound literal size.
static struct foo x = (struct foo) {1, 'a', 'b'};
static int y[] = (int []) {1, 2, 3};
static int z[] = (int [3]) {1};
Note:
The compiler tags on your post include only GCC; however, you make comparisons to C99, (and multiple GCC versions). It is important to note that GCC is quicker to add extended capabilities to its compilers than the larger C standard groups are. This has sometimes lead to buggy behavior and inconsistencies between versions. Also important to note, extensions to a well known and popular compiler, but that do not comply with an accepted C standard, lead to potentially non-portable code. It is always worth considering target customers when deciding to use an extension that has not yet been accepted by the larger C working groups/standards organizations. (See ISO (Wikipedia) and ANSI (Wikipedia).)
There are several examples where the smaller more nimble Open Source C working groups or committees have responded to user base expressed interest by adding extensions. For example, the switch case range extension.
Quoting the C11 standard, chapter §6.5.2.5, Compound literals, paragraph 3, (emphasis mine)
A postfix expression that consists of a parenthesized type name followed by a brace-enclosed list of initializers is a compound literal. It provides an unnamed object whose value is given by the initializer list.
So, a compound literal is tread as an unnamed object, which is not considered a compile time constant.
Just like you cannot use another variable to initialize a static variable, onward C99, you cannot use this compound literal either to initialize a static variable anymore.
Related
I thought C had no more surprises for me, but this surprised me.
const int NUM_FOO = 5;
....
int foo[NUM_FOO];
==>error C2057: expected constant expression
My C++ experience has made me internally deprecate #define as much as possible. So this one was a real surprise. VS2019, compiled with /TC. I thought C99 allowed variable size arrays anyway.
Can anybody explain why the rejection occurs, since the compiler for sure knows at compile time the size of the array?
Is it not the case that C99 allows variable size arrays?
In C, this declaration:
const int NUM_FOO = 5;
doesn't make NUM_FOO a constant expression.
The thing to remember (and yes, this is a bit counterintuitive) is that const doesn't mean constant. A constant expression is, roughly, one that can be evaluated at compile time (like 2+2 or 42). The const type qualifier, even though its name is obviously derived from the English word "constant", really means "read-only".
Consider, for example, that these are a perfectly valid declarations:
const int r = rand();
const time_t now = time(NULL);
The const just means that you can't modify the value of r or now after they've been initialized. Those values clearly cannot be determined until execution time.
(C++ has different rules. It does make NUM_FOO a constant expression, and a later version of the language added constexpr for that purpose. C++ is not C.)
As for variable length arrays, yes, C added them in C99 (and made them optional in C11). But as jamesdlin's answer pointed out, VS2019 doesn't support C99 or C11.
(C++ doesn't support VLAs. This: const int NUM_FOO = 5; int foo[NUM_FOO]; is legal in both C99 and C++, but for different reasons.)
If you want to define a named constant of type int, you can use an enum:
enum { NUM_FOO = 5 };
or an old-fashioned macro (which isn't restricted to type int):
#define NUM_FOO 5
jamesdlin's answer and dbush's answer are both correct. I'm just adding a bit more context.
A variable with the const qualifier does not qualify as a constant expression.
Section 6.6p6 of the C11 standard regarding Constant Expressions states
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, _Alignof expressions, 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 or _Alignof operator
Note that const qualified integer objects are not included.
This means that int foo[NUM_FOO]; is a variable length array, defined as follows from section 6.7.6.2p4:
If the size is not present, the array type is an incomplete type. If
the size is * instead of being an expression, the array type
is a variable length array type of unspecified size, which can
only be used in declarations or type names with function prototype
scope; such arrays are nonetheless complete types. If the size is
an integer constant expression and the element type has a known
constant size, the array type is not a variable length array
type; otherwise, the array type is a variable length array
type.
As for the error you're getting, that is because Visual Studio is not fully compliant with C99 and does not support variable length arrays.
const in C does not declare a compile-time constant. You can use an enum constant instead if you want to avoid using #define and want a symbolic name that can appear in a debugger.
C99 does support VLAs. However, VS2019 does not support C99.
On top of the existing answers which are all good, the reason a const-qualified object fundamentally can't in general be a constant expression, in the sense of "one that can be evaluated at compile time" (as mentioned in Keith's answer), is that it can have external linkage. For example, you can have in foo.c
const int NUM_FOO = 5;
and in bar.c:
extern int NUM_FOO;
...
int foo[NUM_FOO];
In this example, the value of NUM_FOO cannot be known when compiling bar.c; it is not known until you choose to link foo.o and bar.o.
C's model of "constant expression" is closely tied to properties that allow translation units (source files) to be translated (compiled) independently to a form that requires no further high-level transformations to link. This is also why you can't use addresses in constant expressions except for address constant expressions which are limited to essentially the address of an object plus a constant.
I'm dynamically calculating the size of an array. Something like:
void foo(size_t limit)
{
char buffer[limit * 14 + 1];
}
But just GCC compiler says:
error: ISO C90 forbids variable length array ‘buffer’
searching on SO I found this answer:
C99 §6.7.5.2:
If the size is an expression that is not an integer constant
expression... ...each time it is evaluated it shall have a value
greater than zero.
So, I did the re-declaration of size limit type variable to:
void foo(const size_t limit)
But it continues to give warning for me. Is this a GCC bug?
const-qualifying a variable doesn't make it a compile-time constant (see C99 6.6 §6 for the defintion of an integer constant expression), and before the introduction of variable-length arrays with C99, array sizes needed to be compile-time constants.
It's rather obvious that const-qualify a variable doesn't make it a compile-time constant, in particular in case of function parameters which won't be initialized until the function is called.
I see the following solutions to your problem:
compile your code as C99 via -std=c99 or -std=gnu99
allocate your buffer via malloc()
use alloca() if available, which is the closest you can come to variable-length arrays with C90
choose a maximum buffer size which is always used and fail if the given limit argument overflows
As a side note, even though C99 allows variable-length arrays, it's still illegal to use the value of an integer variable with static storage duration as size for an array with static storage duration, regardless of const-qualification: While there's nothing which prevents this in principle if the integer variable is initialized in the same translation unit, you'd have to special-case variables with visible defintion from those whose definition resides in a different translation unit and would either have to disallow tentative defintions or require multiple compilation passes as the initialization value of a tentatively defined variable isn't known until the whole translation unit has been parsed.
const does not introduce a constant in C but a read-only variable.
#define SIZE 16
char bla[SIZE]; // not a variable length array, SIZE is a constant
but
const int size = 16;
char bla[size]; // C99 variable length array, size is a constant
C90 doesn't allow variable length arrays. However, you can use the c99-gcc compiler to make this work.
You are compiling with c90-gcc but looking at C99 specifications.
No it is not a bug. You can't use a VLA in C90. When you declared
const size_t limit
that is not a constant expression. A constant expression would be something like a literal value 666.
Note that C differs significantly from C++ in this regard. Even a constant like this
const int i = 666;
is not a constant expression in C. This is the primary reason why constant values are typically declared with #define in C.
As written in your question, this is from C99, not C90, you need to compile it against C99 to be able to use variable length arrays.
A const qualified variable is not an integer constant expression in the sense of the standard. This has to be a literal constant, an enumeration constant, sizeof or some expression composed with these.
Switch to C99 if you may. The gcc option is -std=c99 (or gnu99 if you want gnu extension.)
I thought C had no more surprises for me, but this surprised me.
const int NUM_FOO = 5;
....
int foo[NUM_FOO];
==>error C2057: expected constant expression
My C++ experience has made me internally deprecate #define as much as possible. So this one was a real surprise. VS2019, compiled with /TC. I thought C99 allowed variable size arrays anyway.
Can anybody explain why the rejection occurs, since the compiler for sure knows at compile time the size of the array?
Is it not the case that C99 allows variable size arrays?
In C, this declaration:
const int NUM_FOO = 5;
doesn't make NUM_FOO a constant expression.
The thing to remember (and yes, this is a bit counterintuitive) is that const doesn't mean constant. A constant expression is, roughly, one that can be evaluated at compile time (like 2+2 or 42). The const type qualifier, even though its name is obviously derived from the English word "constant", really means "read-only".
Consider, for example, that these are a perfectly valid declarations:
const int r = rand();
const time_t now = time(NULL);
The const just means that you can't modify the value of r or now after they've been initialized. Those values clearly cannot be determined until execution time.
(C++ has different rules. It does make NUM_FOO a constant expression, and a later version of the language added constexpr for that purpose. C++ is not C.)
As for variable length arrays, yes, C added them in C99 (and made them optional in C11). But as jamesdlin's answer pointed out, VS2019 doesn't support C99 or C11.
(C++ doesn't support VLAs. This: const int NUM_FOO = 5; int foo[NUM_FOO]; is legal in both C99 and C++, but for different reasons.)
If you want to define a named constant of type int, you can use an enum:
enum { NUM_FOO = 5 };
or an old-fashioned macro (which isn't restricted to type int):
#define NUM_FOO 5
jamesdlin's answer and dbush's answer are both correct. I'm just adding a bit more context.
A variable with the const qualifier does not qualify as a constant expression.
Section 6.6p6 of the C11 standard regarding Constant Expressions states
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, _Alignof expressions, 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 or _Alignof operator
Note that const qualified integer objects are not included.
This means that int foo[NUM_FOO]; is a variable length array, defined as follows from section 6.7.6.2p4:
If the size is not present, the array type is an incomplete type. If
the size is * instead of being an expression, the array type
is a variable length array type of unspecified size, which can
only be used in declarations or type names with function prototype
scope; such arrays are nonetheless complete types. If the size is
an integer constant expression and the element type has a known
constant size, the array type is not a variable length array
type; otherwise, the array type is a variable length array
type.
As for the error you're getting, that is because Visual Studio is not fully compliant with C99 and does not support variable length arrays.
const in C does not declare a compile-time constant. You can use an enum constant instead if you want to avoid using #define and want a symbolic name that can appear in a debugger.
C99 does support VLAs. However, VS2019 does not support C99.
On top of the existing answers which are all good, the reason a const-qualified object fundamentally can't in general be a constant expression, in the sense of "one that can be evaluated at compile time" (as mentioned in Keith's answer), is that it can have external linkage. For example, you can have in foo.c
const int NUM_FOO = 5;
and in bar.c:
extern int NUM_FOO;
...
int foo[NUM_FOO];
In this example, the value of NUM_FOO cannot be known when compiling bar.c; it is not known until you choose to link foo.o and bar.o.
C's model of "constant expression" is closely tied to properties that allow translation units (source files) to be translated (compiled) independently to a form that requires no further high-level transformations to link. This is also why you can't use addresses in constant expressions except for address constant expressions which are limited to essentially the address of an object plus a constant.
I am aware of the limitations for initializers at file-scope in c: you cannot use a variable (even const), functions etc... But I really do not understand why this does not work:
#include <stdlib.h>
#include <stdio.h>
//unsigned long a = "string"[0] > '0'; // this does not compile
unsigned long a = 's' > '0'; // this works fine, output is "a = 1"
int main(void)
{
printf("a = %lu\n",a);
return 0;
}
Why does the line with the string literal gives: error: initializer element is not constant. Are string literals not considered constant? Is there any way to make it work?
Thanks in advance
Your variable has static storage duration, as such and according to N1570 (C11) §6.7.9/p4:
All the expressions in an initializer for an object that has static or
thread storage duration shall be constant expressions or string
literals.
String literals have static storage duration §6.4.5/p6, so their addresses can be considered constant expressions (which is why they are allowed as initializers). But you are trying to access the value at such an address, and the C standard explicitly forbids it. To quote §6.6/p9, emphasis mine:
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.
On the other hand, when you used character constants for comparison, you obtain a valid constant expression.
In C language objects with static storage duration have to be initialized with constant expressions or with aggregate initializers containing constant expressions.
Now here you had basically "string"[0] as an constant expression? But is it so?
The thing is From 6.6p2
A constant expression can be evaluated during translation rather than runtime, and accordingly may be used in any place that a constant may be.
Now after that I checked the translation phases and what it consists of:
It will be clear that none of the expression is evaluated which involves a [] array subscripting over string literal. This literally involves dereferencing an address and getting the vlaue which is not possible to do in translation phases. That's why this error.
I'm dynamically calculating the size of an array. Something like:
void foo(size_t limit)
{
char buffer[limit * 14 + 1];
}
But just GCC compiler says:
error: ISO C90 forbids variable length array ‘buffer’
searching on SO I found this answer:
C99 §6.7.5.2:
If the size is an expression that is not an integer constant
expression... ...each time it is evaluated it shall have a value
greater than zero.
So, I did the re-declaration of size limit type variable to:
void foo(const size_t limit)
But it continues to give warning for me. Is this a GCC bug?
const-qualifying a variable doesn't make it a compile-time constant (see C99 6.6 §6 for the defintion of an integer constant expression), and before the introduction of variable-length arrays with C99, array sizes needed to be compile-time constants.
It's rather obvious that const-qualify a variable doesn't make it a compile-time constant, in particular in case of function parameters which won't be initialized until the function is called.
I see the following solutions to your problem:
compile your code as C99 via -std=c99 or -std=gnu99
allocate your buffer via malloc()
use alloca() if available, which is the closest you can come to variable-length arrays with C90
choose a maximum buffer size which is always used and fail if the given limit argument overflows
As a side note, even though C99 allows variable-length arrays, it's still illegal to use the value of an integer variable with static storage duration as size for an array with static storage duration, regardless of const-qualification: While there's nothing which prevents this in principle if the integer variable is initialized in the same translation unit, you'd have to special-case variables with visible defintion from those whose definition resides in a different translation unit and would either have to disallow tentative defintions or require multiple compilation passes as the initialization value of a tentatively defined variable isn't known until the whole translation unit has been parsed.
const does not introduce a constant in C but a read-only variable.
#define SIZE 16
char bla[SIZE]; // not a variable length array, SIZE is a constant
but
const int size = 16;
char bla[size]; // C99 variable length array, size is a constant
C90 doesn't allow variable length arrays. However, you can use the c99-gcc compiler to make this work.
You are compiling with c90-gcc but looking at C99 specifications.
No it is not a bug. You can't use a VLA in C90. When you declared
const size_t limit
that is not a constant expression. A constant expression would be something like a literal value 666.
Note that C differs significantly from C++ in this regard. Even a constant like this
const int i = 666;
is not a constant expression in C. This is the primary reason why constant values are typically declared with #define in C.
As written in your question, this is from C99, not C90, you need to compile it against C99 to be able to use variable length arrays.
A const qualified variable is not an integer constant expression in the sense of the standard. This has to be a literal constant, an enumeration constant, sizeof or some expression composed with these.
Switch to C99 if you may. The gcc option is -std=c99 (or gnu99 if you want gnu extension.)