You should not be surprised by the equality of a and *a in this context. There is nothing surprising in this. In the C language, a “two-dimensional array” is simply an ordinary one-dimensional array, whose elements are also one-dimensional arrays.
A one-dimensional array of, say, double d[10] is simply a flat, continuous, compact memory block, of size 10 * sizeof(double) , consisting of ten closely related double elements. At the same time, the address of the whole array &d coincides numerically with the address of its zero element &d[0] , because they “start” at the same point of memory. The situation is completely analogous to the equality of pointers in this example.
struct S { int a; } s; printf("%p %p\n", (void *) &s, (void *) &s.a); // Оба указателя совпадают численно
The address of the entire object of the struct type is numerically the same as the address of the very first field inside this object. Quite similarly, the address of the entire array is numerically the same as the address of the very first (zero) element of this array.
At the same time, in the C language, the expression d type " double [10] array double [10] " in most contexts (not all) is implicitly reduced to type double * - a pointer pointing to the zero element of the array. Those. in most contexts, the expression d behaves equivalently to the expression &d[0] , i.e. array looks like a pointer outwardly. This phenomenon is called array type decay . (It is for this reason that arrays in C are often confused with pointers, although in reality they are different types and there are no pointers in arrays.)
All this is immediately applicable to two-dimensional arrays (and multidimensional arrays), because, as mentioned above, in C, a “two-dimensional array” is a structure that is recursive in its structure: it is simply a one-dimensional array, whose elements are one-dimensional arrays. The above array type decay phenomenon works the same at any level of this recursion.
The above expression a initially has the type int [3][2] , but in this context it is implicitly cast to the type "pointer to the zero element of the array a ". This pointer is of type int (*)[2] and points to a subarray a[0] type int [2] as part of a .
The expression *a initially has the type int [2] - this is the subarray a[0] in the composition of a , but in this context *a implicitly reduced to the type "pointer to the zero element of the array a[0] ". This is an int * type pointer that points to the very first int in the a[0] subarray, i.e. on a[0][0] .
Both a , and a[0] , and a[0][0] begin at the same memory point, for which reason the numeric representations of these pointers are the same
printf("%p %p %p\n", (void *) &a, (void *) a, (void *) *a); // Все три указателя совпадают численно