Const arrays in C - 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

Related

How can I distinguish a pointer from a dereference in C?

int value =5;
void testPointer( int* pa, int* pb) {
*pa = *pb +5;
*pb = value;
value += 10;
}
How can I distingush both from each other? I dont get it
A unary * always indicates a dereference.
This may feel familiar and intuitive in an expression: *pb + 5 means to get the value pb points to and add five. In contrast, you may find a declaration less intuitive; what does * mean in int *pa?
The way to think of this is that a declaration gives a picture of how something will be used. The declaration int *pb says *pb will be used as an int. In other words, when we get the value pb points to, it is an int. The * in a declaration represents the same thing that happens in an expression: dereferencing the pointer.
Kernighan and Ritchie tell us this in The C Programming Language, 1978, page 90:
The declaration of the pointer px is new.
int *px;
is intended as a mnemonic; it says the combination *px is an int, that is, if px occurs in the context *px, it is equivalent to a variable of the type int. In effect, the syntax of the declaration for a variable mimics the syntax of expressions in which the variable might appear.
As a more involved example, consider int (*p)[];. This tells us that (*p)[] is an int. Since [] is used to access array elements, this means (*p) must be an array of int. And that means p must be a pointer to an array of of int. Just like *, [] does not have a reversed meaning in declarations. It does not mean “is an array of” instead of “access an element of”; it is still an image of how the thing will be used in an expression.
When specifying a type, for example inside a declaration, the * means "pointer". Otherwise, the * means "dereference" or "multiplication" (depending on the context).
For example, when initializing a variable inside a declaration, all * before the = means "pointer", and all * after the = means "dereference" or "multiplication":
int i = 80;
int *p = &i; // In this case, * means "pointer"
int j = *p; // In this case, * means "dereference", so j gets the value 80

What is the use of the const keyword when assigning structure values?

What is the use of const keyword in this program?
#include <stdio.h>
struct x{
int a;
int b;
};
int main()
{
struct x xx;
xx = (const struct x) {0};
xx.a = 10;
printf("%d, %d", xx.a, xx.b);
}
There is no difference in output when I run without const keyword. What is its significance here?
C 2018 6.3.2.1 2 says qualifiers such const are removed when any object is used for its value in an expression:
Except when it is the operand of the sizeof operator, the unary & operator, the ++ operator, the -- operator, or the left operand of the . operator or an assignment operator, an lvalue that does not have array type is converted to the value stored in the designated object (and is no longer an lvalue); this is called lvalue conversion. If the lvalue has qualified type, the value has the unqualified version of the type of the lvalue; additionally, if the lvalue has atomic type, the value has the non-atomic version of the type of the lvalue; otherwise, the value has the type of the lvalue.
Thus, in xx = (const struct x) {0};, (const struct x) {0}; is a compound literal that creates a const struct x object. However, in taking the value of this object, const is removed, and the result is a struct x value. (The “value” of a structure is the aggregate of the values of all its members.)
Thus, in this use, the const ultimately has no effect. However, the compound literal does create a const object. The effect of this could be seen in other uses. For example, with const struct x *p = & (const struct x) {0};, we take the address of the compound structure. Then if we attempt to remove const and modify the structure, as with ((struct x *) p)->a = 3;, the behavior is not defined by the C standard. The compiler could have placed the structure in read-only memory, and attempting to modify it could generate a trap. Or, during optimization, the compiler could have used the fact that the structure is const to ignore any possibility it could change. For example, in:
const struct *p = & (const struct x) {0};
((struct x *) p)->a = 3;
printf("%d\n", p->a);
the program might print “0” because in the p->a in the printf, the compiler uses the fact that p->a is defined to be a const zero.
But what is it's significance here?
None.
When you assign to xx like xx = something; you need that something to have the basic type struct x. That happens due to the cast. So if you do
(struct x) {0};
an anonymous object of type struct x will be created and it can be assigned to xx like:
xx = (struct x) {0};
Now adding a const qualifier in the cast means that the anonymous object is a constant. However, it doesn't really matter that it's constant as you don't write to it anyway. The only operation performed on the anonymous object is the read operation performed during the assignment to xx. For an assignment it makes no difference whether the source object (RHS object) is a constant or not.
So: No, for your code const has no significance.
Can we construct code where it matters?
Yes, consider this (obfuscated useless) code:
int y;
y = ++(struct x) {0, 42}.b;
printf("%d\n", y);
It will create an anonymous object with values .a=0 and .b=42 and then increment and return .b. In other words, it will print 43.
If you add const to the cast, you'll get a compiler error like:
main.cpp:26:9: error: increment of member 'b' in read-only object
26 | y = ++(const struct x) {0, 42}.b;
| ^~
So here the const keyword did matter.
This will zero-initialize a temporary struct x:
xx = (const struct x) {0};
and that temporary will then be used to copy assign xx. That's what it will do no matter if the temporary object is const or not.
An example with a popular compiler (gcc) shows that it'll simply zero out the two members in xx, in both the const and non-const case without actually creating a temporary object. The compiled program acts as-if it did though:
xor edx, edx
xor esi, esi

Why are those definitions not legal in 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.

Array declared without size and const volatile

I found this line in my work project, but I can't understand this:
extern const volatile uint8 * const volatile array[];
Could you please help me explain the line?
First, leaving out the qualifiers:
uint8 *array[];
array is an array of unspecified size whose elements are of type uint8 *. In other words, an array of pointers to uint8.
If this declaration appears as a parameter to a function, the array syntax is actually shorthand for a pointer. If it is not a function parameter and is declared at file scope then this acts as a tentative definition. A complete definition may occur elsewhere in the code that specifies the size and optionally an initializer. If no other complete definition exists, then the array is defined to have 1 element.
Before talking about what the declaration means with the qualifiers, let's first talk about exactly what those qualifiers mean.
The const qualifier prevents code from modifying the named object. Given this declaration:
const int x;
It means that x can't be modified using for example x = 1. With pointers involved it's a little more tricky:
const int *x;
This defines x as a pointer to a const int. That means that you can modify x but not what it points to. So x = &y is legal but not *x = 1.
int * const x;
This defines x as a const pointer to an int, which means you can't modify x but you can modify what it points to. So x = &y is not legal but *x = 1 is.
const int * const x;
This defines x as a const pointer to a const int. So in this case, neither x nor what it points to can be modified.
const int * const x[];
Here, x is an array whose elements are const pointers to a const int. As in the prior example, for each array element, neither the array element nor what it points to can be modified.
Now let's talk about volatile. This qualifier tells the compiler that the variable in question might change unpredictably. From section 6.7.3p7 of the C standard:
An object that has volatile-qualified type may be modified in
ways unknown to the implementation or have other unknown side
effects. Therefore any expression referring to such an object shall
be evaluated strictly according to the rules of the abstract machine,
as described in 5.1.2.3. Furthermore, at every sequence point the
value last stored in the object shall agree with that prescribed by
the abstract machine, except as modified by the unknown factors
mentioned previously. 134) What constitutes an access to an
object that has volatile-qualified type is implementation-defined
134) A volatile declaration may be used to describe an object
corresponding to a memory-mapped input/output port or an object
accessed by an asynchronously interrupting function. Actions
on objects so declared shall not be "optimized out" by an
implementation or reordered except as permitted by the rules for
evaluating expressions
What this means is that a volatile object could change in ways not known to the compiler, and thus the compiler should not perform any optimizations on this variable and in fact should assume the value was changed externally.
Now moving on the your full declaration:
const volatile uint8 * const volatile array[];
This declares array as an array of unspecified size whose elements are of type uint8 *, where the elements of the array cannot be modified by the program (i.e. are const) but could be modified externally (i.e. volatile), and what those array elements point to also cannot be changed by the program but could be modified externally.

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)".

Resources