Why are those definitions not legal in C? - c

I'm reading a book about C and I don't understand this concept:
Another common misconception is thinking of a const qualified variable as a constant expression. In C, const means "read-only", not "compile time constant". So, global definitions like const int SIZE = 10; int global_arr[SIZE]; and const int SIZE = 10; int global_var = SIZE; are not legal in C.
I also don't understand very good the diference between const variable and constant expression. All const variables are constant expressions, right? I have readed other questions about this topic but I still without understantig. Thanks.

suppose you have
int a = 42;
const int *b = &a;
now *b is const ie read-only. You are not allowed to change *b without casting the const away (thanks Eric Postpischil)
// *b = -1; // not allowed
a = -1;
printf("%d\n", *b); // print -1
The point is: the value of a const qualified object may change. A constant value never changes.

What they basically mean is, In C it is illegal to use a const qualified variable to initialize another variable or determine the size of an array with it at global scope, like for example:
const int SIZE = 5;
int a = SIZE; // This is not allowed.
int b[SIZE]; // This is also not allowed.
int main(void)
{
...
}
This is because variables and arrays at global scope need to be determinate at compilation time. A const qualified variable is still a variable and the values of variables are computed/evaluated at run-time.
A macro constant, which is a "compile time constant" could be used for this like f.e.:
#define SIZE 15
int a[SIZE]; // This is ok.
int b = SIZE; // This is ok, too.
I also don't understand very good the difference between const variable and constant expression. All const variables are constant expressions, right?
No.
Quote from ISO:IEC 9899/2018 (C18), Section 6.6/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."
A constant expression is a literal expression which always get evaluated to the same value - the evaluated value is constant. Therefore it can be evaluated at compile time.
F.e.:
5 + 4
is always 9 and can therefore be evaluated at compilation time.
Whereas a const variable:
const int SIZE = 5;
or
(const int SIZE 5;)
5 + 9 + SIZE;
is not a constant expression as it implies a variable. Although the variable SIZE is qualified by const (which means that it can´t be modified after initialization), it is not a constant expression, because a variable, does not matter if it const or not, is computed/evaluated at run-time.
A const qualified variable is not nor can be a part of a constant expression.

Related

runtime const value set for arr size and no error

#include <stdio.h>
int func()
{
int a = 3, b = 4;
int c = a * b;
return c;
}
int main()
{
const int N = 10;
int arr[N];
printf("size = %ld\n", sizeof(arr));
int x = 10;
const int SIZE = x;
int buf[SIZE];
printf("size = %ld\n", sizeof(buf));
const int FN = func();
int buf2[FN];
printf("size = %ld\n", sizeof(buf2));
return 0;
}
ubuntu 20 5.4.0-42-generic
gcc 9.3.0
compile:
gcc const_create_arr.c -Wall
show no warning
output:
size = 40
size = 40
size = 48
output corret
the last one FN is init by func(). we know that func() return vlaue should be computed in runtime. But an array definition should provide the true length of this array to compiler to help compiler allocate space. So I think the last one should not be passed at compiling. But it seems corret. I want to know how it works. Whether my gcc has optimise it and compute the func return when it compiling.
In all three cases you're creating a variable length array. For an array to not be a VLA the size needs to be an integer constant expression, and a variable with the const qualifier (no matter how it's initialized) does not qualify as one.
The definition of a VLA can be found in section 6.7.6.2p4 of the C standard regarding array declarators:
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.
And the definition of an integer constant expression is given in section 6.6p6:
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.
There is nothing in this definition that qualifies a const qualified variable as an integer constant expression, so the sizeof operator in each of the three cases is being evaluated at runtime because the arrays are VLAs.
C has variable length arrays, so int x[y] is valid C provided y is defined in advance. It doesn't matter if y is const or not, it just needs to be > 0 to make any sense, as well as small enough that you don't use up the entire stack.
In this case y is 12, so you get a length 48 (12 * sizeof(int) where that's 4). This gets computed after the fact, not in advance like you might expect for something trivial like sizeof(int).
In C++ this is not the case as defined by the standard, however some compilers will still do it the C way.

Initializer is not constant... I know, but I feel like this should work, why doesn't it

The following code compiles fine if I remove the static qualifier, but I don't understand why it doesn't work otherwise. I can see the string in the .rodata section (when compiling without the static qualifier), and the final assembly just loads pointers appropriately before calling printf.
#include <stdio.h>
#define S "Testing"
#define C 't'
typedef struct {
char const * const s;
char const c;
} foo;
int main(int argc, char **argv)
{
static foo const f = {
.s = S,
.c = S[3],
};
printf("s = %s, c = %c\n", f.s, f.c);
return 0;
}
Also, it compiles fine with the static qualifier if I use:
.c = C,
Interestingly, the assembly looks identical (yeah, gcc hijacks the 't' from "Testing")
This snippet is just to demonstrate the specific example I'm encountering in a much more complex problem. Why can't the compiler treat a string literal [] element as a constant, when clearly it does?
BTW, compiled with gcc 5.4 with only -O2
Edit: To clarify, the problem is the initialization of f.c
Edit: Thanks for the insights everyone. In summary, the preprocessor can't do anything with strings, which is lame. If I actually implement what I am trying to do in the scope of a function, the compiler (with optimization) will eventually process everything down to constants in the .rodata section, but I can't accomplish the same at the global scope, unfortunately.
Solution: Write a custom python script (or whatever your favorite scripting language is) to generate code, or use c++
In C language objects with static storage duration require constant expressions as initializers. S[3] is not a constant expression in C. You are basically not allowed to "read from memory" in constant expressions, i.e. you are not allowed to use unary * operator and, consequently, not allowed to use [] operator to read array elements.
Meanwhile, 't' is immediately a constant, which is why your code compiles when you replace S[3] with C.
From C11 6.7.9/4 (Initialization - Constraints):
All the expressions in an initializer for an object that has static or thread storage duration shall be constant expressions or string literals.
The definition of constant expression is found in 6.6. Specifically 6.6/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.
However S[3] is none of those. An arithmetic constant expression is only allowed to have operands that are constants of arithmetic type (in brief), however S[3] means *(S + 3) which has a pointer operand.
Using 't' as initializer is fine, that is a character constant and therefore an arithmetic constant expression.
Note: constant expression has a quite separate meaning to "const-qualified variable". Constant expressions are never const-qualified and are a disjoint set from variables and objects.
Possibly the language could have been defined so that indexing a string literal with a valid index counts as a constant expression, but it wasn't.
Note that string literals are not constant expressions (although their address is). They are arrays of non-const char . There is also a rule that it is undefined behaviour to modify a string literal. So portable code has to treat a string literal as if it were const, although technically it is not. Historically some compilers have allowed modification of string literals.
I am not sure what your eventual goal is, but maybe this small change in your struct can help. According to the standard:
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 the use of these
operators.
ISO/IEC 9899:2011 6.6/9
In the code from your example, you attempt to use the value of S[3] in an initializer as a constant expression. The above shows why this is not possible, but you can use the address of S[3] as a constant expression:
#include <stdio.h>
#define S "Testing"
#define C 't'
typedef struct {
char const * const s;
char const * const c;
} foo;
int main(int argc, char **argv)
{
static foo const f = {
.s = S,
.c = &S[3],
};
printf("s = %s, c = %c\n", f.s, *(f.c));
return 0;
}
When I compile and run this code, the output is:
s = Testing, c = t
You're problem is that define does not initialise just used in preprocessor. You would need:
static const char *S = "Testing/0";
static const char C = 't';

Why is an implicit conversion from non-const to const allowed for pointers in the first place?

I understand that implicit conversion from non-const to const is not dangerous when handling values, by example:
int mutable = 5;
const int immutable = mutable;
However, when working with pointers, I can do something as follows:
int some_number = 5;
int *mutable = &some_number;
const int *immutable = mutable; // <= Legal, but isn't it dangerous?
// Let's try to break const
printf("%d\n", *immutable); // Prints 5
mutable[0] = 10;
printf("%d\n", *immutable); // Prints 10
By the way, for double pointers this is not allowed (at least you get a warning)! See this question and the references therein.
From the C11 standard (draft N1570):
6.7.3 Type qualifiers
Syntax
type-qualifier:
const
restrict
volatile
_Atomic
[...]
Semantics:
The properties associated with qualified types are meaningful only for expressions that are lvalues.
[...]
EXAMPLE 1
An object declared
extern const volatile int real_time_clock;
may be modifiable by hardware, but cannot be assigned to, incremented, or decremented.
In simple terms:
const doesn't mean a value never changes. It only means that you are not allowed to change it1.
For callees , const is a restriction, not a promise.
For callers however, it is a promise. Passing a const pointer to a function, you can safely assume that the function will not change your data2, thus is "making a promise to you".
1 ...through the identifier with the const qualifier.
2 ...through the const argument passed to it.

Why is int x[n] wrong where n is a const value?

I cannot understand why doing this is wrong:
const int n = 5;
int x[n] = { 1,1,3,4,5 };
even though n is already a const value.
While doing this seems to be right for the GNU compiler:
const int n = 5;
int x[n]; /*without initialization*/
I'm aware of VLA feature of C99 and I think it's related to what's going on but
I just need some clarification of what's happening in the background.
The key thing to remember is that const and "constant" mean two quite different things.
The const keyword really means "read-only". A constant is a numeric literal, such as 42 or 1.5 (or an enumeration or character constant). A constant expression is a particular kind of expression that can be evaluated at compile time, such as 2 + 2.
So given a declaration:
const int n = 5;
the expression n refers to the value of the object, and it's not treated as a constant expression. A typical compiler will optimize a reference to n, replacing it by the same code it would use for a literal 5, but that's not required -- and the rules for whether an expression is constant are determined by the language, not by the cleverness of the current compiler.
An example of the difference between const (read-only) and constant (evaluated at compile time) is:
const size_t now = time(NULL);
The const keyword means you're not allowed to modify the value of now after its initialization, but the value of time(NULL) clearly cannot be computed until run time.
So this:
const int n = 5;
int x[n];
is no more valid in C than it would be without the const keyword.
The language could (and IMHO probably should) evaluate n as a constant expression; it just isn't defined that way. (C++ does have such a rule; see the C++ standard or a decent reference for the gory details.)
If you want a named constant with the value 5, the most common way is to define a macro:
#define N 5
int x[N];
Another approach is to define an enumeration constant:
enum { n = 5 };
int x[n];
Enumeration constants are constant expressions, and are always of type int (which means this method won't work for types other than int). And it's arguably an abuse of the enum mechanism.
Starting with the 1999 standard, an array can be defined with a non-constant size; this is a VLA, or variable-length array. Such arrays are permitted only at block scope, and may not have initializers (since the compiler is unable to check that the initializer has the correct number of elements).
But given your original code:
const int n = 5;
int x[n] = { 1,1,3,4,5 };
you can let the compiler infer the length from the initializer:
int x[] = { 1,1,3,4,5 };
And you can then compute the length from the array's size:
const int x_len = sizeof x / sizeof x[0];
Why int x[n] is wrong where n is a const value?
n is not a constant. const only promise that n is a 'read-only' variable that shouldn't be modified during the program execution.
Note that in c, unlike c++, const qualified variables are not constant. Therefore, the array declared is a variable length array.
You can't use initializer list to initialize variable length arrays.
C11-§6.7.9/3:
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.
You can use #define or enum to make n a constant
#define n 5
int x[n] = { 1,1,3,4,5 };
If you are exhaustively initialising an array, then it is easier, safer and more maintainable to let the compiler infer the array size:
int x[] = { 1,1,3,4,5 };
const int n = sizeof(x) / sizeof(*x) ;
Then to change the array size you only have to change the number of initialisers, rather than change the size and the initialiser list to match. Especially useful when there are many initialisers.
Even though n is a const, you cannot use it to define the size of an array unless you wish to create a VLA. However, you cannot use an initializer list to initialize a VLA.
Use a macro to create a fixed size array.
#define ARRAY_SIZE 5
int x[ARRAY_SIZE] = { 1,1,3,4,5 };
Is your code semantically different from myfunc() here:
void myfunc(const int n) {
int x[n] = { 1,1,3,4,5 };
printf("%d\n", x[n-1]);
*( (int *) &n) = 17; // Somewhat less "constant" than hoped...
return ;
}
int main(){
myfunc(4);
myfunc(5);
myfunc(6); // Haven't actually tested this. Boom? Maybe just printf(noise)?
return 0;
}
Is n really all that constant? How much space do you think the compiler should allocated for x[] (since it is the compiler's job to do so)?
As others have pointed out, the cv-qualifier const does not mean "a value that is constant during compilation and for all times after". It means "a value that the local code is not supposed to change (although it can)".

Const arrays in C

Original question:
If I define:
const int z[5] = {10, 11, 12, 13, 14};
does it mean:
it's a constant array of integers i.e. the address which z points to is always constant and can never change, but the elements of z can change.
OR
Each element of z is a constant i.e. their value can never change.
Edit:
More info:
There is another variable:
const int *y = z;
func((int *) y);
where func is defined as:
void func(int y[]) {
int i;
for(i = 0; i < 5; i++) {
y[i] = i; //y[i] can be set to any integer; used i as example
}
}
where in func, using y, the array is traversed and each element is changed. Is this is valid even though all elements of z are const?
It means that each element of z is read-only.
The object z is an array object, not a pointer object; it doesn't point to anything. Like any object, the address of z does not change during its lifetime.
Since the object z is an array, the expression z, in most but not all contexts, is implicitly converted to a pointer expression, pointing to z[0]. That address, like the address of the entire array object z, doesn't change during the object's lifetime. This "conversion" is a compile-time adjustment to the meaning of the expression, not a run-time type conversion.
To understand the (often confusing) relationship between arrays and pointers, read section 6 of the comp.lang.c FAQ.
It's important to understand that "constant" and const are two different things.
If something is constant, it's evaluated at compile time; for example, 42 and (2+2) are constant expressions.
If an object is defined with the const keyword, that means that it's read-only, not (necessarily) that it's constant. It means that you can't attempt to modify the object via its name, and attempting to modify it by other means (say, by taking its address and casting to a non-const pointer) has undefined behavior. Note, for example, that this:
const int r = rand();
is valid. r is read-only, but its value cannot be determined until run time.
In your case the answer is:
Each element of z is a constant i.e. their value can never change.
You can't create a const array because arrays are objects and can only be created at runtime and const entities are resolved at compile time.
So, the const is interpreted as in the first example below, i.e. applied for the elements of the array. Which means that the following are equivalent:
The array in your example needs to be initialized.
int const z[5] = { /*initial (and only) values*/};
const int z[5] = { /*-//-*/ };
It is some type commutative property of the const specifier and the type-specifier, in your example int.
Here are few examples to clarify the usage of constant:
1.Constant integers definition: (can not be reassigned). In the below two expression the use of const is equivalent:
int const a = 3; // after type identifier
const int b = 4; // equivalent to before type qualifier
2.Constant pointer definition (no pointer arithmetics or reassignment allowed):
int * const p = &anInteger; // non-constant data, constant pointer
and pointer definition to a constant int (the value of the pointed integer cannot be changed, but the pointer can):
const int *p = &anInteger; // constant data, non-constant pointer

Resources