If we draw out your array on "paper" it will look like this
+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
| arr2D[0][0] | arr2D[0][1] | arr2D[0][2] | arr2D[1][0] | arr2D[1][1] | arr2D[1][2] | arr2D[2][0] | arr2D[2][1] | arr2D[2][2] |
+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
Then you have to remember that an array naturally decays to a pointer to its first element. That is plain arr2D
when a pointer is expected, is the same as &arr2D[0]
.
Now if we "redraw" the array, but only for arr2D[0]
(which is what is most relevant for your question) with some of the possible pointers:
+-------------+-------------+-------------+-----+
| arr2D[0][0] | arr2D[0][1] | arr2D[0][2] | ... |
+-------------+-------------+-------------+-----+
^
|
&arr2D[0]
|
&arr2D[0][0]
Since we know that arr2D
is the same as &arr2D[0]
, we can then do that substitution in the expression arr2D == *arr2D
. That gets us &arr2D[0] == *&arr2D[0]
.
The dereference *
and address-of &
operators cancel each other out, so we have &arr2D[0] == arr2D[0]
.
Now keep up... We know that an array decays to a pointer to its first element, and we know that arr2D[0]
is an array; That means it will decay to &arr2D[0][0]
, leaving us with the expression &arr2D[0] == &arr2D[0][0]
. And as shown those two addresses are the same, which means the comparison will be true.
Important note: While &arr2D[0]
and &arr2D[0][0]
might both point to the same location, their types are different. The type of &arr2D[0]
is int (*)[3]
, while &arr2D[0][0]
is of type int *
.
Armed with the above information it should be easy to decipher the other comparison *arr2D == arr2D[0]
, especially since all of the parts of that are already mentioned.