10

これは常にそうですか、つまり、配列名は常に配列の最初の要素へのポインターです.なぜそうなのですか?それは実装のようなものですか、それとも言語機能ですか?

4

3 に答える 3

16

配列名自体はポインターではありませんが、ほとんどのコンテキストで配列の最初の要素へのポインターに変化します。言語がそのように定義しているため、そのようになっています。

C11 6.3.2.1 Lvalues, arrays, and function designators、段落 3 から:

sizeof演算子、_Alignof演算子、または単項演算子のオペランドである場合、または配列の初期化に使用される文字列リテラルである場合を除き、&"型の配列" 型を持つ式は、"型へのポインター" 型を持つ式に変換されます " 配列オブジェクトの最初の要素を指し、左辺値ではありません。

comp.lang.c FAQのArrays and Pointersセクションから、このトピック (および関連する微妙な動作に関する多く) の詳細を学ぶことができます。

編集上の余談: C++ でも同じ種類の動作が行われますが、言語の仕様は少し異なります。参考までに、私がここに持っている C++11 ドラフトから、4.2 Array-to-pointer conversion、段落 1:

N Tタイプ「配列」または「未知境界の配列」の左辺値または右辺値は、T「ポインタ」タイプの右辺値に変換できますT。結果は、配列の最初の要素へのポインターです。

于 2013-08-29T18:18:50.387 に答える
10

この動作の歴史的な理由については、こちらを参照してください。

C は、B (go figure) という名前の以前の言語から派生したものです。B は型のない言語であり、メモリは基本的に符号なし整数である「セル」の線形配列として扱われました。

B で、次のように N 要素の配列を宣言した場合

auto a[10];

N 個のセルが配列に割り当てられ、変数 にバインドされた最初の要素のアドレスを格納するために別のセルが確保されましたa。C と同様に、配列のインデックス付けはポインター演算によって行われました。

a[j] == *(a+j)

Ritchie が構造体型を C に追加し始めるまで、これはかなりうまく機能していました。論文で彼が示した例は、仮想のファイル システム エントリで、ノード ID の後に名前が続きます。

struct {
  int inumber;
  char name[14];
};

彼は、構造体型の内容をディスク上のデータと一致させたいと考えていました。整数の 2 バイトの直後に、名前の 14 バイトが続きます。配列の最初の要素へのポインターを格納する適切な場所がありませんでした。

それで彼はそれを取り除きました。ポインター用のストレージを確保する代わりに、彼はポインター値が配列式自体​​から計算されるように言語を設計しました。

ちなみに、これが配列式を代入のターゲットにできない理由です。事実上、書き込みと同じこと3 = 4;です。値を別の値に代入しようとすることになります。

于 2013-08-29T19:31:24.397 に答える
2

Carl Norum は、質問に対して言語弁護士の回答を提供しました (そして、私の支持を得ました)。実装の詳細な回答は次のとおりです。

コンピューターにとって、メモリ内のオブジェクトは単なるバイト範囲であり、メモリ処理に関する限り、最初のバイトへのアドレスとバイト単位のサイズによって一意に識別されます。メモリ内にがある場合でもint、そのアドレスは最初のバイトのアドレス以上でも以下でもありません。サイズはほとんどの場合暗黙的です。ポインタを に渡すintと、コンパイラはそのアドレスのバイトが として解釈されることを知っているため、そのサイズを認識しますint。同じことが構造体にも当てはまります。アドレスは最初のバイトのアドレスであり、サイズは暗黙的です。

現在、言語設計者は、構造体で行ったのと同様のセマンティックを配列で実装できた可能性がありますが、それには正当な理由がありませんでした。コピーは、ポインターを渡すだけに比べて現在よりもさらに非効率的でした.構造体は、ほとんどの場合ポインターであり、配列は通常、大きくなるように意図されています。言語ごとに値のセマンティクスを強制するには、非常に大きくなります。

したがって、配列の名前が実質的にポインターと同等になるように指定することにより、配列は常にメモリ オブジェクトであることが強制されていました。配列と他のメモリ オブジェクトとの類似性を壊さないようにするために、サイズは再び暗黙的であると言われました (プログラマーではなく、言語の実装に対して!): コンパイラーは、配列が渡されたときに配列のサイズを忘れることができます配列内にいくつのオブジェクトがあったかを知るために、プログラマーに頼ります。

これには、配列へのアクセスが非常に単純であるという利点がありました。それらは、インデックスを配列内のオブジェクトのサイズで乗算し、そのオフセットをポインターに追加するという、ポインター演算の問題に崩壊します。a[5]が とまったく同じである理由は5[a]、 の短縮形です*(a + 5)

パフォーマンスに関連するもう 1 つの側面は、配列からサブ配列を作成するのが非常に簡単であることです。開始アドレスのみを計算する必要があります。データを新しい配列にコピーすることを強制するものは何もありません。正しいサイズを使用することを覚えておく必要があります...

そうです、そうです、実装の単純さとパフォーマンスの観点から、配列名がそのようにポインターに減衰することには深い理由があり、私たちはそれを喜んでいるはずです。

于 2013-08-29T19:04:24.340 に答える