C - initializer not constant with string literal - c

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.

Related

why am i getting warnings errors due to assignment of a variable's address to a pointer in the global scope?

this the snip of the warning i was getting when i tried to compile the program.I am just getting started with pointers and this following program is being flagged by compiler for some reason I am not able comprehend. the code is as follows:
#include <stdio.h>
int dec = 0;
int *d;
d = &dec;
int main() {
return 0;
}
there is no error when I am stuffing these declarations in to main's body. the version of gcc I am using is gcc version 12.2.0(downloaded using MSYS2) and code editor MS visual code.can anybody post an explanation for this?
as i have stated above i have randomly started typing a program to get familiar with pointers, i expected there to be no variation in the treatment of pointers regardless of where they are being declared and intialised.
You're attempting to perform an assignment outside of a function, which is not allowed. What you can do is initialize:
int *d = &dec;
You may use only declarations in file scopes.
In the provided program you are using an assignment statement
d = &dec;
in the file scope. So the compiler issues an error.
Instead you could write for example
#include <stdio.h>
int dec = 0;
int *d = &dec;
int main( void ) {
return 0;
}
As the variable dec has static storage duration then the expression &dec is an address constant and my be used as an initializer for the variable d that also has static storage duration.
From the C Standard (6.7.9 Initialization)
4 All the expressions in an initializer for an object that has static
or thread storage duration shall be constant expressions or string
literals.
and (6.6 Constant expressions)
7 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.
and
9 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.

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.

Initialize static variable with element of const compound literal

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.

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