ポインタと配列はまったく別のものです。この 2 つの類似点や混乱は、C 言語の産物です。
ポインターは、別の変数の場所を保持する変数です。配列 (C の場合) は、同じ型の値の集合体であり、メモリ内に連続して割り当てられます。
配列は、基本要素へのポインタに対する算術演算によってアクセスされます[0]
。配列を参照する式が評価される場合、出現する値は element へのポインター[0]
です。
int array[10];
int *p = array; /* array expression yields pointer to array[0] */
int *q = &array[0]; /* q also points to same place as p */
C の配列表記は、実際にはポインターで機能する偽物です。構文E1[E2]
は と同じことを意味します*(E1 + E2)
(E1 と E2 が十分に括弧で囲まれているため、結合性と優先順位に惑わされる必要はありません)。 を介して要素のアドレスを取得する場合&E1[E2]
、これは と同じ&*(E1 + E2)
です。address-of と dereference は「取り消す」ままにしE1 + E2
ます。したがって、これらも同等です。
int *r = array + 3;
int *q = &array[3];
array[i]
とは両方とも有効な構文であるためpointer[i]
、初心者段階 (構文をセマンティクスと誤解している) の人々は、配列とポインターが何らかの形で同じものであると結論付けます。
アセンブリ言語でのプログラミングに時間を割いてください。アセンブリ言語では、次のようなストレージを定義できます。
A: DFS 42 ;; define 42 words, labelled as A.
次に、次のように使用します。
MOV R13, A ;; address of A storage is moved into R13
MOV R1, [R13 + 3] ;; load fourth word, A[3]
R13 はストレージを指します。それは Aがポインタであることを意味しません。A はストレージの名前です。もちろん、ストレージを使用するには、その実効アドレスが必要なので、A への参照はそれに解決されます。コードがアセンブルされてリンクされると、そのMOV
命令は最終的に数値アドレスを R13 にロードします。
C は単なる高レベルのアセンブリ言語です。配列は、実効アドレス (ポインター データ型) に解決される名前付きストレージのようなものです。
配列は常に実効アドレスに解決されるとは限りません。sizeof a
は配列のサイズをa
バイト単位でsizeof p
計算しますが、ポインター データ型のサイズを計算しますp
。
もう 1 つの違いは、配列の名前でその配列以外の場所を参照することはできませんが、値をポインターに代入できることです。
array++; /* invalid */
array = &array[0]; /* invalid */
p = &array2[0]; /* valid */
p++; /* valid: point to the next element in array2 */