まったく初めてのようですので、厳密な説明ではなく、簡単な言葉で説明させてください。
ご覧のとおり、上記のプログラムでは、aと&aは同じ数値を持ちます。混乱の原因はそこにあると思います。それらが同じである場合、次のアドレスは両方の場合の後に次のアドレスを与える必要があるのではないかと思うかもしれません。aポインター演算:
(&a+1) and (a+1)
でもそうじゃない!!配列のベースアドレス(aここ) と配列のアドレスは同じではありません! a数値的&aには同じかもしれませんが、同じタイプではありません。aは型char*で&aあり、型は ですchar (*)[5]。つまり、&aは (のアドレス) へのポインタであり、サイズ 5 の配列です。しかしa、ご存知のように、 は配列の最初の要素のアドレスです。数値的には、下の^を使ったイラスト。
しかし、これら 2 つのポインタ/アドレスをインクリメントすると、つまり(a+1)とのよう(&a+1)に、算術演算がまったく異なります。最初のケースでは、配列内の次の要素のアドレスに「ジャンプ」しますが、後者の場合は、次のように 5 要素ずつジャンプします。それは、5 つの要素の配列のサイズです! .わかった?
1 2 3 4 5
^ // ^ stands at &a
1 2 3 4 5
^ // ^ stands at (&a+1)
1 2 3 4 5
^ //^ stands at a
1 2 3 4 5
^ // ^ stands at (a+1)
以下は、以下のようにサイズを明示的に指定しないと、(&a+1) のようなものが発生したときに、プログラムが「ジャンプ」する要素の数がわからないことを意味するため、配列の未指定の境界に関するエラーが発生します。
char a[]={1,2,3,4,5};
char *ptr=(char *)(&a+1); //(&a+1) gives error as array size not specified.
次に、ポインタ/アドレスを(ptr-1)次のようにデクリメントする部分に進みます。最初のケースでは、デクリメント部分に到達する前に、型にキャストされる上記のステートメントで何が起こるかを知っておく必要がありますchar*。
char *ptr=(char *)(&a+1);
ここで何が起こるかというと、元typeの(&a+1)型を「剥ぎ取り」、それを配列のベースアドレスと同じchar (*)[5]型にキャストすることです。(配列のベースアドレスの違いに注意してください。と配列のアドレス .したがって、上記のステートメントのキャストと代入の後、のデクリメントが続き、配列の最後の要素の直後のメモリ位置が得られます。char*aprintf()ptr5
1 2 3 4 5
^ // ^ stands at location of 5, so *ptr gives 5
したがって、ポインターptrをデクリメントした後にポインターを逆参照すると、期待どおり*(ptr-1)の値が出力5されます。
最後に、 が印刷されている 2 番目のケースと比較してください。記号^1を使用して示した図を見てください。としてインクリメントした場合、配列の 2 番目の要素を指します。つまり、このアドレスを に割り当てました 。したがって、2番目のケースで逆参照すると、 が得られます。aa+12ptrptr(ptr-1)1ptr1
1 2 3 4 5
^ // ^ stands at address of 1, so *ptr gives 1
これですべてが明確になったことを願っています。