さて、この質問には回答があり、回答が受け入れられましたが、受け入れられた回答でさえ、元の投稿者が見た奇妙な結果を説明していません: なぜ5 と 8y[1]
をy[2]
印刷するのですか? これが説明です。
元の投稿者: 次のステートメントからどのような結果が得られますか?
printf ("Size of integer: %zu\n", sizeof (int));
printf ("Size of pointer: %zu\n", sizeof (int*));
出力は次のようになります。
Size of integer: 4
Size of pointer: 8
つまり、整数のサイズが 4 バイトでポインターのサイズが 8 バイトの 64 ビット マシンでコンパイルしていると推測しています。その仮定に基づいて、これが起こっていることです。
p
配列です。いくつかの例外を除いて、任意の式で使用される場合、配列の名前はその最初の要素へのポインターに "崩壊" します。p
したがって、の値にアクセスするたびに、最初の要素のアドレスが生成されます。
&p
ポインターへの配列の「崩壊」に関する規則の例外の 1 つです。address-of 演算子を配列の名前に適用すると、配列の最初の要素へのポインターではなく、配列全体へのポインターが返されます。
これが意味することは、p
と&p
は同じ値を持ちますが、意味的には大きく異なるということです。印刷すると同じ値が得られます。
printf("p value is %p and p points to %p", p, &p); // use %p and not %d for addresses
p
ただし、これは同じものを&p
指すという意味ではありません。p
配列の最初の要素のアドレス、つまり&p[0]
. 一方、は5 つの整数の配列全体&p
のアドレスです。
したがって、 and を次のように定義するx
とy
、次のようになります。
int* x = p;
int** y = &p;
x
配列の最初の要素へのポインターが割り当てられます。y
配列全体へのポインタが割り当てられます。これは重要な違いです!
y
さらに、宣言方法とそれに割り当てる値の間に不一致があります。&p
タイプint (*) [5]
です。5 の配列へのポインタint
。y
は、単一の へのポインターへのポインターにすぎませんint
。コンパイラは、この不一致に関する警告を表示する必要があります。私は:
Warning: incompatible pointer types assigning to 'int**' from 'int (*) 5'
y[1]
この不一致により、との値を出力する際の奇妙な結果が説明されy[2]
ます。値に何が起こっているかを見てみましょう。
ご存じのとおり、配列の添字は配列の先頭からのオフセットです。
x[0] == *(x + 0)
したがってx[0]
、配列の最初の要素、つまり 2 が得られます。
x[1] == *(x + 1)
しかしx
、intへのポインタです。では、追加で実際に何が起こっているのx + 1
でしょうか? ポインター演算がどのように機能するかを覚えておいてください。ポインタに整数を追加するということは、実際には、その整数倍の要素のサイズを追加していることを意味します。この場合:
x + 1 == x + (1 * sizeof(int))
sizeof(int)
お使いのシステムでは は 4 であるため、 の値は配列x[1]
内の次の整数であり、3 です。
では、 を印刷y[0]
すると、これはどのように評価されるのでしょうか。
y[0] == *(y + 0)
したがって、 が指すアドレスy
、つまりのアドレスにある値p
が出力されます。これは の最初の要素なp
ので、結果は 2 になります。
印刷するとどうなりますy[1]
か?
y[1] == *(y + 1)
しかし、何y
ですか?ですpointer to a pointer to an int
。したがって、 に 1 を追加するとy
、ポインター演算の動作は、再び 1 * が指している要素の型のサイズを追加することになります。
y + 1 == y + (1 * sizeof (int*))
an のサイズはint*
4 バイトではなく 8 バイトです。したがって、1 ずつインクリメントするたびy
に、8 バイト、つまり2 つの整数のサイズがインクリメントされます。したがって、その値を逆参照すると、配列内の次の整数ではなく、2 つ離れた整数が取得されます。
より明確に説明するには: 配列が要素 1000 で始まると仮定します。次に、それぞれint
が 4 バイトかかるため、次のようになります。
Address Element
-----------------------
1000 2
1004 3
1008 5
1012 6
1016 8
p == &p == x == y == 1000
*x == *y == 2
x に 1 を足すと、1 * が足されます。sizeof(int)
つまり、実際には 4 が足されます。*(x + 1)
x[1]
しかし、y に 1 を追加すると、1 * が追加されますsizeof(int*)
。つまり、実際には 8 が追加されます。したがって、1008 が得られ*(y + 1)
、アドレス 1008 または 5 の要素が得られます。
これにより、得られる出力が説明されます。ただし、これは合理的なコーディング方法ではありません。ポインターのサイズが常に 8 バイトになるとは思わないでください。を に割り当てないでint (*) []
くださいint**
。へのポインターへのポインターを逆参照して、結果int
を取得することを期待しないでint
ください。また、常にコンパイラの警告に注意してください。