Reputation: 37257
Consider this code snippet:
void foo(int a[], int b[]){
static_assert(sizeof(a) == sizeof(int*));
static_assert(sizeof(b) == sizeof(int*));
b = a;
printf("%d", b[1]);
assert(a == b); // This also works!
}
int a[3] = {[1] = 2}, b[1];
foo(a, b);
Output (no compilation error):
2
I can't get the point why b = a
is valid. Even though arrays may decay to pointers, shouldn't they decay to const pointers (T * const
)?
Upvotes: 5
Views: 390
Reputation: 222933
Yes, it would have made sense for array parameters declared with []
to be adjusted to const-qualified pointers. However, const
did not exist when this behavior was established.
When the C language was being developed, it made sense to pass an array by passing its address, or, more specifically, the address of the first element. You certainly did not want to copy the entire array to pass it. Passing the address was an easy way to make the array known to the called function. (The semantics for the reference types we see in C++ had not been invented yet.) To make that easy for programmers, so that they could write foo(ArrayA, ArrayB)
instead of foo(&Array[0], &ArrayB[0])
, the mechanism of converting an array to a pointer to its first element was invented. (Per M.M. and The Development of the C Language by Dennis M. Ritchie, this notation for parameters already existed in C’s predecessor language, B.)
That is fine, you have hidden the conversion. But that is only where the function is called. In the called routine, the programmer who is thinking about passing an array is going to write void foo(int ArrayA[], int ArrayB[])
. But since we are actually passing pointers, not arrays, these need to be changed to int *ArrayA
and int *ArrayB
. So the notion that parameters declared as arrays are automatically adjusted to pointers was created.
As you observe, this leaves the programmer able to assign values to the parameters, which changes the apparent base address of the array. It would have made sense for a parameter declared as int ArrayA[]
to be adjusted to int * const ArrayA
, so that the value of the parameter ArrayA
could not be changed. Then it would act more like an array, whose address also cannot be changed, so this better fits the goal of pretending to pass arrays even though we are passing addresses.
However, at the time, const
did not exist, so this was not possible, and nobody thought of inventing const
at that time (or at least did work on it enough to get it adopted into the language).
Now there is a large amount of source code in the world that works with the non-const adjustment. Changing the specification of the C language now would cause problems with the existing code.
Upvotes: 4
Reputation: 134346
Quoting C11
, chapter §6.7.6.3, Function declarators (including prototypes)
A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to type’’, where the type qualifiers (if any) are those specified within the
[
and]
of the array type derivation. [...]
So, a
and b
are actually pointers, not arrays.
There's no assignment to any array type happennning here, hence there's no problem with the code.
Upvotes: 8
Reputation: 141618
Arrays cannot be assigned to. There are no arrays in the foo
function. The syntax int a[]
in a function parameter list means to declare that a
has type "pointer to int
". The behaviour is exactly the same as if the code were void foo(int *a, int *b)
. (C11 6.7.6.3/7)
It is valid to assign one pointer to another. The result is that both pointers point to the same location.
Even though arrays may decay to pointers, shouldn't they decay to const pointers (T * const)?
The pointer that results from array "decay" is an rvalue. The const
qualifier is only meaningful for lvalues (C11 6.7.3/4). (The term "decay" refers to conversion of the argument, not the adjustment of the parameter).
Upvotes: 10