The only reason to compile your definitions is because your C compiler is too permissive when it comes to pointer type conversions. If you use some switches that make it more pedantic in this regard, he should immediately tell you that only the third initialization is valid, and the rest are erroneous.
( ), T[N], "" ( ) T * - , . , A A &A[0]. , , - &, sizeof , char.
array - int [2][2]. int (*)[2].
int *pointer = array;
int (*)[2], - int *. . .
int *ppppointer = &array[0];
: int (*)[2]. .
&array int (*)[2][2]. ,
int *ppointer = &array;
.
yo -
int *pppointer = array[0];
array[0] - int [2], int * type - , .
, , "". , .
int *pppointer = &array[0][0];
, . , "" (array[0] &array[0][0]) .
,
int (*pointer)[2] = array;
int (*ppointer)[2][2] = &array;
int (*ppppointer)[2] = &array[0];
int *. , -, int * .