すべての定義をコンパイルする唯一の理由は、ポインター型の変換に関して C コンパイラーが許可しすぎていることです。この点でよりペダンティックにするいくつかのスイッチを使用すると、3 番目の初期化のみが有効であり、残りは誤りであることがすぐにわかるはずです。
ほとんどのコンテキスト (いくつかの例外を除く) では、型の配列が式で使用されると、ポインター型 (最初の要素を指すT[N]ポインター) に "減衰" (暗黙的に変換) されます。T *つまり、任意の array のこのようなコンテキストではA、A式は と同等&A[0]です。配列型の減衰が発生しない唯一のコンテキストは、配列の初期化子として使用される単項&演算子、sizeof演算子、および文字列リテラルcharです。
あなたの例arrayでは、タイプの値ですint [2][2]。初期化の右側で使用すると、ポインター型に減衰しますint (*)[2]。このため、これは無効です
int *pointer = array;
右側がint (*)[2]、左側が ですint *。これらは異なるポインター型です。一方を他方で初期化することはできません。
の
int *ppppointer = &array[0];
は前のものとまったく同じです: 右側はint (*)[2]type の値を生成します。まったく同じ理由で無効です。
式は型の&arrayポインターを生成しますint (*)[2][2]。繰り返しますが、この理由から
int *ppointer = &array;
無効です。
あなたの例にある唯一の有効な初期化は
int *pppointer = array[0];
array[0]は型の表現であり、int [2]型に崩壊しint *ます - 左側にあるのと同じ型です。
つまり、ここではどちらが「優れている」かという問題はありません。初期化の 1 つだけが有効で、他は違法です。有効な初期化は、次のようにも記述できます。
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 *具体的に必要です。