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)".
Related
when I wrote this code in c
#include <stdio.h>
int main(void) {
const int k = 7;
int a[k];
k = 9;
return 0;
}
there was an error saying that the k in the brackets needs to be a constant value(expression must have a constant value) and the other k needs to be a modifiable value(expression must be a modifiable lvalue). This doesn't make any sense. Am I doing something wrong? I thought that you can put constant variables as the size of an array.
const-qualifying a variable does not make it a compile time constant, so int a[k] declares a variable length array. Variable lenghth arrays are only supported in C99 and later and not all compilers support them.
Also, with k = 9, you are trying to modify a const-qualified variable, which is not allowed.
Solution would be to either use C99 or later and a compiler that supports variable length arrays or (in this case probably better) to define the array size as a preprocessor constant (e.g. #define k 10) .
Am I doing something wrong? I thought that you can put constant variables as the size of an array.
Yes, in C99 and later when variable length arrays (VLA) are supported.
Earlier versions of MSVC do not support VLA nor C99.
As of 2020 Astute readers will note that VLAs are also not supported in MSVC C11.
why does it say that it needs to be modifiable and it needs to be a constant
These are 2 different problems in 2 lines of code.
When variable length arrays (VLA) are not supported, an array declaration width must be a constant. const int k is not a constant.
const int k = 7;
int a[k]; // k is not a constant.
// Alternate
#define K 7
int a[K]; // K is a constant.
Modifying a const object is prohibited.
const int k = 7;
...
k = 9;
Instead,
int k = 7;
...
k = 9;
This is what I write:
const int MAX=100;
int main (){
int notas [MAX]={0};
The compiler says the following:
[Error] variable-sized object may not be initialized
[Warning] excess elements in array initializer
When I write MAX with #define MAX 100, it works. But I don´t understand what's the matter with doing it this way?
In this case
const int MAX=100;
does not create a compile time constant, so the array is treated as VLA. By definition, VLAs can not be initialised, hence the error.
On the other hand, #define MAX 100 is a pre-processor macro, and based on the textual replacement property, it results in a compile time constant value of 100, then the array is not a VLA and can be initialized as per the initialization rules.
This
const int MAX=100;
int main (){
int notas [MAX]={0};
is a declaration of a variable length array the size of which is determined at run-time because the declaration of the variable MAX is not a compile-time constant in C. Such arrays may not be initialized in declarations.
From the C Standard (6.7.9 Initialization)
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.
So you could write for example
const int MAX=100;
int main (){
int notas [MAX];
memset( notas, 0, MAX * sizeof( int ) );
Otherwise you could use a compile time constant like
enum { MAX=100 };
int main (){
int notas [MAX]={0};
Despite the const in the declaration
const int MAX = 100;
MAX is not a constant expression (i.e., something whose value is known at compile time). Its value isn't known until run time, so the declaration of notas is treated as a variable-length array declaration, and a VLA declaration may not have an initializer (nor may a VLA be declared at file scope, nor may it be a member of a struct or union type).
With the preprocessor macro
#define MAX 100
all instances of the symbol MAX are replaced with the literal 100 after preprocessing, so it's effectively the same as writing
int notas[100] = {0};
which is why using the preprocessor macro works.
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.
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
Can anyone explain what a variably modified type is?
If we have an array a[n] and n is not known at compile time then a is a VLA. Given an array b[c][d] where c and d are not known until runtime implies b is a VLA, right?
In my book they have said that a variably modified type contains a VLA.
That's it; nothing more.
How do I create a pointer to a variably modified type?
A variably-modified type is a VLA (variable length array). There's a similar type in a structure with a flexible array member, but I don't plan to discuss flexible array members further.
The key point about a VLA is that the dimension of an array is not known until run-time. Classically, in C89 and before the standard, all dimensions of an array except the first had to be a known constant value at compile time (and the first dimension could be specified as int a[] or int b[][SIZE] or int c[][SIZE1][SIZE2] where the sizes are constants).
void some_function(int n)
{
int a[n];
int c = n+1;
int d = n+2;
int b[c][d];
another_function(n, a, c, d, b);
...
}
void another_function(int n, int a[n], int c, int d, int b[c][d])
{
...
}
Both a and b are variable length arrays. Prior to C99, you could not have written some_function() like that; the size of the arrays would have to be known at compile time as compile-time constants. Similarly, the notation for another_function() would not have been legal before C99.
You could, and still can (for reasons of backwards compatibility, if nothing else) write a moderate simulation of another_function():
enum { FIXED_SIZE = 32 };
void yet_another_function(int a[], int n, int b[][FIXED_SIZE], int c)
{
...
}
This isn't a perfect simulation because the FIXED_SIZE is a fixed size, but the pure C99 VLA code has a variable dimension there. Old code would often, therefore, use a FIXED_SIZE that was large enough for the worst case.
Inside another_function(), the names a and b are basically pointers to variably modified types.
Otherwise, you do it the same as for a fixed size array:
int z[FIXED_SIZE];
int (*z_pointer)[FIXED_SIZE] = &z;
int v[n];
int (*v_pointer)[n] = &v;
VLA == Variable Length Array
Variable Length Arrays were introduced in the C99 spec to allow for things like this:
int someArraySize;
int myArray[someArraySize];
Variably Modified type is the type of a Variable Length Array. Thus, a Variably Modified type CONTAINS a VLA. In the case of your example of b[c][d] where c and d are not known until run time, b is a Variably Modified type that happens to be a Variable Length multi-dimensional array. b[c][d] is a variable length array of variable length arrays-- phew, what a mouthful.
Here is a great source I found that describes these VLAs and the Variably Modified type with examples:
http://gustedt.wordpress.com/2011/01/09/dont-be-afraid-of-variably-modified-types/
VMT is a type usually used to allocate heap blocks of VMT size. The pointer to VMT is not VLA.
#include <stdlib.h>
int main( const int argc, char * const argv[argc])
{
typedef char * VMT [argc] ;
VMT * vmt_ptr = malloc(sizeof(VMT));
* vmt_ptr[0] = argv[0] ;
free(vmt_ptr);
return 42;
}
Some people prefer to call them "VLA on the heap". For some people that defeats the purpose of VLAs. For them, VLA is a small(er) array on the stack.
{ // VMT is a type of VLA
VMT VLA ;
VLA[0] = argv[0] ;
}
No mem leak here. But then some people are wondering what the fuss is all about.
{
typedef char * VMT [argc] ;
VMT * vmt_ptr = alloca(sizeof(VMT));
* vmt_ptr[0] = argv[0] ;
}
They are using alloca, using the VMTs in the process. Effectively creating VMT pointers to blocks allocated on stack space.
There are valid use cases for all three snippets. I hope also showing what are the VMT's.
Mandatory Godbolt: https://godbolt.org/z/zGe4K5hez