Initialize static variable with element of const compound literal - c

Is a const compound literal a valid initializer for a static variable?
#define COMPOUND ((const int [2]){1, 2})
static const int x = COMPOUND[0];
/* static const int x = 1; should be equivalent */
EDIT:
The possible duplicacte in the first comment doesn't make sense, because I'm asking explicitly about const literals, and not variables.

Yes, an element of a compound literal may be used as an initializer.
C 2018 6.7.9 4 tells us what initializers must be:
All the expressions in an initializer for an object that has static or thread storage duration shall be constant expressions or string literals.
6.6 tells us what constant expressions may be. Paragraph 3 says:
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.
Paragraph 4 says:
Each constant expression shall evaluate to a constant that is in the range of representable values for its type.
Paragraph 7 expands this to:
More latitude is permitted for constant expressions in initializers. 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.
None of the other paragraphs prohibit the use of compound literals, so they are permitted.

Related

using sizeof() to initiate an array

I just found out that I could use sizeof() to initiate an array but not strlen(), why is this?
char str[] = " ## Aab, ~bccdD>> e", str2[sizeof(str)]={-1};
if I use strlen(), it would give me this error, but shouldn't the return value of sizeof be a variable too?
error: variable-sized object may not be initialized
char str[] = " ## Aab, ~bccdD>> e", str2[strlen(str)]={-1};
sizeof is an operator, not a function, and with one exception its value is known at compile time. When used as an array size, it’s similar to using a constant expression like 10.
Since strlen is a function, it isn’t evaluated until run time. As of C99, you can declare an array with a runtime value as the size - these are called variable-length arrays. While useful, VLAs have some limitations, one of which is that you cannot declare them with an initializer. You would have to set the initial value using memset or something like that:
char str[] = " ## Aab, ~bccdD>> e", str2[sizeof(str)];
memset( str2, -1, sizeof str2 );
The one exception to sizeof being evaluated at runtime is when it is used on a VLA, since the size of a VLA isn’t established until runtime.
C 2018 6.7.9 says a variable length array may not be initialized:
The type of the entity to be initialized shall be an array of unknown size or a complete object type that is not a variable length array type.
C 2018 6.7.6.2 4 says that if an array is declared with a size that is not an integer constant expression, it is a variable length array:
… 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…
Constant expressions are limited by C 2018 6.6 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.
Thus, using strlen(str) makes the array a variable length array, which you cannot initialize. I do not know the exact motivation for this prohibition, but I will note that allowing initialization of variable length arrays could require the compiler to generate more code than is typical for C definitions.
That suffices to explain the error message, but I will note that integer constant expressions are further limited by C 2018 6.6 4:
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.

array bound is not an integer constant before ']' token when it is actually constant

I am trying to have an array, which has a defined size known during compile time.
const uint8_t a[2] = {0, 127}; // Fine
const uint8_t aRange = a[1] - a[0]; // Fine
double sums[aRange]; //Fails
But this fails by gcc with
error: array bound is not an integer constant before ']' token.
As a workaround I intend to use macro variables. But would like to know if there is any logic behind it. There is this answer, which is most related. However, according to the answer, it should have worked.
aRange is a constant integer, but not an integer constant. Isn't English a fun language?
C11 §6.4.4.1 Integer constants
C11 §6.6 Constant expressions ¶6
An integer constant expression117) 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.
117) An integer constant expression is required in a number of contexts such as the size of a bit-field member of a structure, the value of an enumeration constant, and the size of a non-variable length array. Further constraints that apply to the integer constant expressions used in conditional-inclusion preprocessing directives are discussed in 6.10.1.
C11 §6.7.6.2 Array declarators — one of the more inscrutable parts of the standard. (¶2 is a constraint; ¶4 and ¶5 specify the semantics of array declarators.)
¶2 If an identifier is declared as having a variably modified type, it shall be an ordinary identifier (as defined in 6.2.3), have no linkage, and have either block scope or function prototype scope. If an identifier is declared to be an object with static or thread storage duration, it shall not have a variable length array type.
¶4 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;143) 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. (Variable length arrays are a conditional feature that implementations need not support; see 6.10.8.3.)
143) Thus, * can be used only in function declarations that are not definitions (see 6.7.6.3).
¶5 If the size is an expression that is not an integer constant expression: if it occurs in a declaration at function prototype scope, it is treated as if it were replaced by *; otherwise, each time it is evaluated it shall have a value greater than zero. The size of each instance of a variable length array type does not change during its lifetime. Where a size expression is part of the operand of a sizeof operator and changing the value of the size expression would not affect the result of the operator, it is unspecified whether or not the size expression is evaluated.
At file (global) scope, you must have an integer constant expression for the dimensions of an array. In a local variable in C99 or later, what you've written would be OK for a VLA (variable-length array).
You could work around this with:
enum { A_MIN = 0, A_MAX = 127 };
const uint8_t a[2] = { A_MIN, A_MAX };
const uint8_t aRange = a[1] - a[0];
double sums[A_MAX - A_MIN];
In C, you can't write const uint8_t aRange = a[1] - a[0]; at file (global) scope, so your code should have been OK unless you're using an antiquated C compiler that doesn't recognize C99 or later (or it defines __STDC_NO_VLA__).
Jonathan's answer is accepted. As a workaround I used macros though.
#define A_MIN 0
#define A_MAX 127
const uint8_t a[2] = {A_MIN, A_MAX};
const uint8_t aRange = A_MAX - A_MIN;
double sums[aRange];

C - initializer not constant with string literal

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.

Not a constant initializer element?

I encountered a confusing case when I was doing semantic analysis for my compiler course.
#include <stdio.h>
int a = "abcd"[2];
int main()
{
char b = "abcd"[2];
printf("%d\n%c\n", a, b);
return 0;
}
GCC says "error: initializer element is not constant" for variable "a".
Why?
The C language requires initializers for global variables to be constant expressions. The motivation behind this is for the compiler to be able to compute the expression at compile time and write the computed value into the generated object file.
The C standard provides specific rules for what is a constant expression:
An
integer constant expression117)
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
.
More latitude is permitted for constant expressions in initializers. 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.
As you can see non of the cases include an array access expression or a pointer dereference. So "abcd"[2] does not qualify as a constant expression per the standard.
Now the standard also says:
An implementation may accept other forms of constant expressions.
So it would not violate the standard to allow "abcd"[1] as a constant expression, but it's also not guaranteed to be allowed.
So it's up to you whether or not to allow it in your compiler. It will be standard compliant either way (though allowing it is more work as you need another case in your isConstantExpression check and you need to actually be able to evaluate the expression at compile time, so I'd go with disallowing it).
int a = "abcd"[2];
a is a global variable initilize at compile time but the "abcd"[2] is computed at run time.
char b = "abcd"[2];
here b is local variable and it initilize at run time after "abcd"[2] computed.

Array as compound literal [duplicate]

This question already has answers here:
Why are compound literals in C modifiable
(2 answers)
Closed 4 years ago.
In C99 we can use compound literals as unnamed array.
But are this literals constants like for example 100, 'c', 123.4f, etc.
I noticed that I can do:
((int []) {1,2,3})[0] = 100;
and, I have no compilation error and is guessable that the first element of that unnamed array is modified with 100.
So it seems as array as compound literal are lvalue and not constant value.
It is an lvalue, we can see this if we look at the draft C99 standard section 6.5.2.5 Compound literals it says (emphasis mine):
If the type name specifies an array of unknown size, the size is
determined by the initializer list as specified in 6.7.8, and the type
of the compound literal is that of the completed array type. Otherwise
(when the type name specifies an object type), the type of the
compound literal is that specified by the type name. In either case,
the result is an lvalue.
If you want a const version, later on in the same section it gives the following example:
EXAMPLE 4 A read-only compound literal can be specified through
constructions like:
(const float []){1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6}
We can find an explanation of the terminology in this Dr Dobb's article The New C: Compound Literals and says:
Compound literals are not true constants in that the value of the
literal might change, as is shown later. This brings us to a bit of
terminology. The C99 and C90 Standards [2, 3] use the word “constant”
for tokens that represent truly unchangeable values that are
impossible to modify in the language. Thus, 10 and 3.14 are an integer
decimal constant and a floating constant of type double, respectively.
The word “literal” is used for the representation of a value that
might not be so constant. For example, early C implementations
permitted the values of quoted strings to be modified. C90 and C99
banned the practice by saying that any program than modified a string
literal had undefined behavior, which is the Standard’s way of saying
it might work, or the program might fail in a mysterious way. [...]
As far I remeber you are right, compound literals are lvalues*, you can also take pointer of such literal (which points to its first element):
int *p = (int []){1, 2, 3};
*p = 5; /* modified first element */
It is also possible to apply const qualifier on such compound literal, so elements are read-only:
const int *p = (const int []){1, 2, 3};
*p = 5; /* wrong, violation of `const` qualifier */
*Note this not means it's automatically modifiable lvalue (so it can used as left operand for assignment operator) since it has array type and refering to C99 draft 6.3.2.1 Lvalues, arrays, and function designators:
A modifiable lvalue is an lvalue that does not have array type, [...]
Referring to the C11 standard draft N1570:
Section 6.5.2.5p4:
In either case, the result is an lvalue.
An "lvalue" is, roughly, an expression that designates an object -- but it's important to note that not all lvalues are modifiable. A simple example:
const int x = 42;
The name x is an lvalue, but it's not a modifiable lvalue. (Expressions of array type cannot be modifiable lvalues, because you can't assign to an array object, but the elements of an array may be modifiable.)
Paragraph 5 of the same section:
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.
The section describing compound literals doesn't specifically say that whether the unnamed object is modifiable or not. In the absence of such a statement, the object is taken to be modifiable unless the type is const-qualified.
The example in the question:
((int []) {1,2,3})[0] = 100;
is not particularly useful, since there's no way to refer to the unnamed object after the assignment. But a similar construct can be quite useful. A contrived example:
#include <stdio.h>
int main(void) {
int *ptr = (int[]){1, 2, 3};
ptr[0] = 100;
printf("%d %d %d\n", ptr[0], ptr[1], ptr[2]);
}
As mentioned above, the array has automatic storage duration, which means that if it's created inside a function, it will cease to exist when the function returns. Compound literals are not a replacement for malloc.
Compound literals are lvalues and it's elements can be modifiable. You can assign value to it. Even pointer to compound literals are allowed.

Resources