配列の名前はCのポインターですか?そうでない場合、配列の名前とポインター変数の違いは何ですか?
配列は配列であり、ポインターはポインターですが、ほとんどの場合、配列名は変換済みポインターになります。よく使用される用語は、ポインタへのdecayです。
配列は次のとおりです。
int a[7];
a
には7つの整数のスペースが含まれており、次のように、割り当てを使用して整数の1つに値を入れることができます。
a[3] = 9;
ポインタは次のとおりです。
int *p;
p
には整数用のスペースは含まれていませんが、整数用のスペースを指すことができます。たとえば、配列a
の最初の場所など、いずれかの場所を指すように設定できます。
p = &a[0];
紛らわしいのは、次のように書くこともできるということです:
p = a;
これはnot配列a
の内容をポインターp
にコピーします(それが意味するものは何でも)。代わりに、配列名a
は、最初の要素へのポインターに変換されます。そのため、その割り当ては前の割り当てと同じです。
これで、配列と同様の方法でp
を使用できます。
p[3] = 17;
これが機能する理由は、Cの配列逆参照演算子[ ]
がポインターの観点から定義されているためです。 x[y]
は、ポインターx
で始まり、ポインターが指すものの後にステップy
要素を進め、そこにあるものは何でも取得します。ポインター算術構文を使用すると、x[y]
は*(x+y)
と書くこともできます。
これがa
などの通常の配列で機能するには、a[3]
の名前a
を最初にポインターに変換する必要があります(a
の最初の要素へ) 。次に、3つの要素を前に進め、そこにあるものをすべて取得します。つまり、配列の3番目の位置にある要素を取得します。 (最初の要素には0の番号が付けられているため、配列の4番目の要素です。)
したがって、要約すると、Cプログラムの配列名は(ほとんどの場合)ポインターに変換されます。例外は、配列でsizeof
演算子を使用する場合です。 a
がこのコンテキストでポインターに変換された場合、sizeof a
は実際の配列ではなくポインターのサイズを与えるため、かなり役に立たないため、その場合はa
は配列自体。
配列が値として使用される場合、その名前は最初の要素のアドレスを表します。
配列が値として使用されない場合、その名前は配列全体を表します。
int arr[7];
/* arr used as value */
foo(arr);
int x = *(arr + 1); /* same as arr[1] */
/* arr not used as value */
size_t bytes = sizeof arr;
void *q = &arr; /* void pointers are compatible with pointers to any object */
配列型の式(配列名など)がより大きな式に現れ、それが&
またはsizeof
演算子のオペランドではない場合、配列式の型が変換されます「N要素のT配列」から「Tへのポインタ」まで。式の値は配列の最初の要素のアドレスです。
要するに、配列名はポインターではありませんが、ほとんどのコンテキストで扱われますあたかもそれはポインターでした。
編集
コメントの質問に答える:
Sizeofを使用する場合、配列の要素のみのサイズをカウントしますか?次に、配列の「ヘッド」も長さとポインターに関する情報でスペースを占有します(これは、通常のポインターよりも多くのスペースを使用することを意味します)。
配列を作成するときに割り当てられるスペースは、要素自体のスペースのみです。別個のポインターまたはメタデータ用にストレージが具体化されることはありません。与えられた
char a[10];
記憶に残るのは
+---+
a: | | a[0]
+---+
| | a[1]
+---+
| | a[2]
+---+
...
+---+
| | a[9]
+---+
expressiona
は配列全体を指しますが、配列要素自体とは別にobjecta
はありません。したがって、sizeof a
は、配列全体のサイズ(バイト単位)を提供します。式&a
は、配列のアドレス最初の要素のアドレスと同じを提供します。 &a
と&a[0]
の違いは、結果のタイプです1 -最初の場合はchar (*)[10]
、2番目の場合はchar *
。
物事がおかしくなるのは、個々の要素にアクセスしたい場合です-a[i]
は*(a + i)
の結果として定義されます-アドレス値a
、オフセットi
要素( not bytes)そのアドレスから結果を逆参照します。
問題は、a
はポインタでもアドレスでもないということです。これは配列オブジェクト全体です。したがって、コンパイラが配列型の式(char [10]
型を持つa
など)を検出するたびに、Cの規則andはその式がオペランドではないsizeof
または単項&
演算子、その式の型はポインター型(char *
)に変換(「減衰」)され、式の値は配列の最初の要素。したがって、expressiona
は、式&a[0]
と同じタイプと値を持ちます(さらに、式*a
は、式と同じタイプと値を持ちます式a[0]
)。
CはBと呼ばれる以前の言語から派生し、Bではa
was配列要素a[0]
、a[1]
などとは別のポインターオブジェクト。 Bの配列セマンティクスを維持するために、別のポインターオブジェクトを格納することを台無しにしたくありませんでした。そこで彼はそれを取り除きました。代わりに、コンパイラは、必要に応じて変換中に配列式をポインター式に変換します。
配列はサイズに関するメタデータを保存しないと言ったことを思い出してください。その配列式がポインタに「減衰」するとすぐに、1つの要素へのポインタが得られます。その要素は要素のシーケンスの最初のものでも、単一のオブジェクトでもかまいません。ポインター自体に基づいて知る方法はありません。
配列式を関数に渡すと、関数が受け取るのは最初の要素へのポインタだけです-配列の大きさがわかりません(これがgets
関数がこのような脅威であり、最終的に削除された理由です)ライブラリから)。関数が配列の要素数を知るには、センチネル値(C文字列の0ターミネーターなど)を使用するか、要素数を個別のパラメーターとして渡す必要があります。
このように宣言された配列
int a[10];
10 int
sのメモリを割り当てます。 a
を変更することはできませんが、a
を使用してポインター演算を行うことはできます。
このようなポインターは、ポインターp
のみにメモリを割り当てます。
int *p;
int
sは割り当てられません。変更できます:
p = a;
そして、次のようにできる限り配列添え字を使用します。
p[2] = 5;
a[2] = 5; // same
*(p+2) = 5; // same effect
*(a+2) = 5; // same effect
配列名自体がメモリの場所を生成するため、配列名をポインタのように扱うことができます。
int a[7];
a[0] = 1976;
a[1] = 1984;
printf("memory location of a: %p", a);
printf("value at memory location %p is %d", a, *a);
そして、ポインターに対してできる他の気の利いたこと(例えば、オフセットの追加/減算)、配列に対してもできます:
printf("value at memory location %p is %d", a + 1, *(a + 1));
言語的には、Cが配列を単に「ポインタ」のようなもの(単なるメモリの場所として公開しなかった場合。メモリ内の任意の場所を指すことはできず、プログラマー)。これを常にコーディングする必要があります。
printf("value at memory location %p is %d", &a[1], a[1]);
この例は問題にいくつかの光を当てると思います。
#include <stdio.h>
int main()
{
int a[3] = {9, 10, 11};
int **b = &a;
printf("a == &a: %d\n", a == b);
return 0;
}
Gcc 4.9.2で(2つの警告を伴って)正常にコンパイルされ、以下を出力します。
a == &a: 1
おっと :-)
したがって、結論はノーです、配列はポインタではなく、メモリに(読み取り専用のものでも)ポインタとして格納されていません、たとえそうであっても、&演算子でそのアドレスを取得できるからです。しかし-おっと-その演算子は動作しません:-))、どちらにしても、あなたは警告されました:
p.c: In function ‘main’:
pp.c:6:12: warning: initialization from incompatible pointer type
int **b = &a;
^
p.c:8:28: warning: comparison of distinct pointer types lacks a cast
printf("a == &a: %d\n", a == b);
C++は、コンパイル時のエラーを伴うこのような試みを拒否します。
編集:
これは私が実証することを意図したものです:
#include <stdio.h>
int main()
{
int a[3] = {9, 10, 11};
void *c = a;
void *b = &a;
void *d = &c;
printf("a == &a: %d\n", a == b);
printf("c == &c: %d\n", c == d);
return 0;
}
c
とa
は同じメモリを「ポイント」していますが、c
ポインターのアドレスは取得できますが、a
ポインターのアドレスは取得できません。