First, you should not use %d
to print the address of a pointer, but %p
. %d
is meant for signed integers that may be or not appropriate to represent an address depending on the implementation. Next you will find interesting references in that other answer from the C tag wiki.
Now for a direct answer. int a[3][3];
declares an array of 3 arrays of three integers. So a[0]
(or *a
) is the first array of three elements.
In printf("%p\n", *a);
the array *a
decays to a pointer to its first element, namely &a[0][0]
, said differently you get the address of the beginning of the array.
In printf("%p\n", a);
the array a
also decays to a pointer to its first element &a[0]
. Here again you get the address of the beginning of the array, that is why the printed values are the same.
But even if they point to same address, &a[0]
and &a[0][0]
are pointers to different types: first points to an array of 3 integers, while second points to an integer. And the same, &a
will still point to the same address, but to still another type and array of 3 arrays to 3 integers.
As the address of an object is the address of its first byte, (char *) a
, (char *) *a
and (char *) &a
are all equal and are a char pointer to the first byte of the array. This is legal because the C language specifies that a pointer to an object can be casted to a pointer to char pointing to the first byte of an object. But it is not allowed to pass a
to a function expecting an int *
and a correct compiler should emit a warning for different indirection levels if you did.