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). Therefore x[0] will give the address of its first element x[0][0] which is 0x500.
  • x[0][0] contains address of an int which is 0x100.
  • x[0][0][0] contains an int value of 10.

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:

  1. You can pass x[0] to the sizeof operator. But that's not really a use of the value, its result depends of the type only.

  2. You can take its address which yields the value of x, i. e. "address of array" with the type int*(*)[5]. In other words: &x[0] <=> &*(x + 0) <=> (x + 0) <=> x

  3. 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 type int**. The effect is the same as if you had casted x to a pointer of type int**.

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 to int;
  • x[0][0] dereferences the outermost pointer and indexes the array, resulting in a pointer to int;
  • 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.