Why is x[0] != x[0][0] != x[0][0][0]?
I'm studying a little of C++ and I'm fighting with pointers. I understand that I can have 3 level of pointers by declaring:
int *(*x)[5];
so that *x
is a pointer to an array of 5 elements that are pointers to int
.
Also I know that x[0] = *(x+0);
, x[1] = *(x+1)
and so on....
So, given the above declaration, why is x[0] != x[0][0] != x[0][0][0]
?
Solution 1:
x
is a pointer to an array of 5 pointers to int
.x[0]
is an array of 5 pointers to int
.x[0][0]
is a pointer to an int
.x[0][0][0]
is an int
.
x[0]
Pointer to array +------+ x[0][0][0]
x -----------------> | | Pointer to int +-------+
0x500 | 0x100| x[0][0]----------------> 0x100 | 10 |
x is a pointer to | | +-------+
an array of 5 +------+
pointers to int | | Pointer to int
0x504 | 0x222| x[0][1]----------------> 0x222
| |
+------+
| | Pointer to int
0x508 | 0x001| x[0][2]----------------> 0x001
| |
+------+
| | Pointer to int
0x50C | 0x123| x[0][3]----------------> 0x123
| |
+------+
| | Pointer to int
0x510 | 0x000| x[0][4]----------------> 0x000
| |
+------+
You can see that
-
x[0]
is an array and will converted to pointer to its first element when used in an expression (with some exceptions). Thereforex[0]
will give the address of its first elementx[0][0]
which is0x500
. -
x[0][0]
contains address of anint
which is0x100
. -
x[0][0][0]
contains anint
value of10
.
So, x[0]
is equal to &x[0][0]
and therefore, &x[0][0] != x[0][0]
.
Hence, x[0] != x[0][0] != x[0][0][0]
.
Solution 2:
x[0] != x[0][0] != x[0][0][0]
is, according to your own post,
*(x+0) != *(*(x+0)+0) != *(*(*(x+0)+0)+0)`
which is simplified
*x != **x != ***x
Why should it be equal?
The first one is the address of some pointer.
The second one is the address of another pointer.
And the third one is some int
value.
Solution 3:
Here is the memory layout of your pointer:
+------------------+
x: | address of array |
+------------------+
|
V
+-----------+-----------+-----------+-----------+-----------+
| pointer 0 | pointer 1 | pointer 2 | pointer 3 | pointer 4 |
+-----------+-----------+-----------+-----------+-----------+
|
V
+--------------+
| some integer |
+--------------+
x[0]
yields "address of array",x[0][0]
yields "pointer 0",x[0][0][0]
yields "some integer".
I believe, it should be obvious now, why they are all different.
The above is close enough for basic understanding, which is why I wrote it the way I wrote it. However, as haccks rightly points out, the first line is not 100% precise. So here come all the fine details:
From the definition of the C language, the value of x[0]
is the whole array of integer pointers. However, arrays are something you can't really do anything with in C. You always manipulate either their address or their elements, never the entire array as a whole:
You can pass
x[0]
to thesizeof
operator. But that's not really a use of the value, its result depends of the type only.You can take its address which yields the value of
x
, i. e. "address of array" with the typeint*(*)[5]
. In other words:&x[0] <=> &*(x + 0) <=> (x + 0) <=> x
In all other contexts, the value of
x[0]
will decay into a pointer to the first element in the array. That is, a pointer with the value "address of array" and the typeint**
. The effect is the same as if you had castedx
to a pointer of typeint**
.
Due to the array-pointer decay in case 3., all uses of x[0]
ultimately result in a pointer that points the beginning of the pointer array; the call printf("%p", x[0])
will print the contents of the memory cells labeled as "address of array".
Solution 4:
-
x[0]
dereferences the outermost pointer (pointer to array of size 5 of pointer to int) and results in an array of size 5 of pointer toint
; -
x[0][0]
dereferences the outermost pointer and indexes the array, resulting in a pointer toint
; -
x[0][0][0]
dereferences everything, resulting in a concrete value.
By the way, if you ever feel confused by what these kind of declarations mean, use cdecl.