配列の名前はCのポインターですか?そうでない場合、配列の名前とポインタ変数の違いは何ですか?
&array[0]
配列ではなくポインタを生成します;)
配列の名前はCのポインターですか?そうでない場合、配列の名前とポインタ変数の違いは何ですか?
&array[0]
配列ではなくポインタを生成します;)
回答:
配列は配列であり、ポインターはポインターですが、ほとんどの場合、配列名はポインターに変換されます。よく使われる用語は、ポインタに腐敗することです。
ここに配列があります:
int a[7];
a
次のように、7つの整数用のスペースが含まれており、割り当てを使用してそれらの1つに値を配置できます。
a[3] = 9;
ここにポインタがあります:
int *p;
p
整数用のスペースは含まれていませんが、整数用のスペースを指すことができます。たとえば、a
最初の場所など、配列内のいずれかの場所を指すように設定できます。
p = &a[0];
混乱する可能性があるのは、次のように書くこともできるということです。
p = a;
これは、配列の内容をポインターにコピーしません(それが何であれ)。代わりに、配列名は最初の要素へのポインターに変換されます。したがって、その割り当ては前の割り当てと同じです。a
p
a
これでp
、配列と同じように使用できます。
p[3] = 17;
これが機能する理由は、Cの配列逆参照演算子[ ]
がポインターで定義されているためです。x[y]
つまり、ポインタx
から始め、ポインタが指すy
ものの前に要素を進め、そこにあるものをすべて取ります。ポインタ算術構文を使用して、x[y]
と書くこともできます*(x+y)
。
これがのような通常の配列で機能するためには、最初にin a
の名前a
をa[3]
(の最初の要素へのa
)ポインタに変換する必要があります。次に、3つの要素を前に進め、そこにあるものをすべて取ります。つまり、配列の3番目の要素を取得します。(最初の要素には0と番号が付けられているため、これは配列の4番目の要素です。)
したがって、要約すると、Cプログラムの配列名は(ほとんどの場合)ポインターに変換されます。1つの例外はsizeof
、配列に対して演算子を使用する場合です。a
このコンテキストでsizeof a
がポインタに変換された場合、実際の配列ではなくポインタのサイズが得られます。これはかなり役に立たないため、その場合a
は配列自体を意味します。
functionpointer()
と(*functionpointer)()
奇妙なことに、同じことを意味します。
sizeof()
配列->ポインターの減衰がない他のコンテキストは演算子です&
-上記の例で&a
は、7の配列int
へのポインターであり、singleへのポインターではありませんint
。つまり、その型はになりますがint(*)[7]
、暗黙的にに変換することはできませんint*
。このように、関数は実際に特定のサイズの配列へのポインターを取得し、型システムを介して制限を強制できます。
配列を値として使用する場合、その名前は最初の要素のアドレスを表します。
配列が値として使用されていない場合、その名前は配列全体を表します。
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 */
配列型(配列名など)の式がより大きな式に現れ、それが&
or sizeof
演算子のオペランドではない場合、配列式の型は "TのN要素配列"から「Tへのポインタ」。式の値は、配列の最初の要素のアドレスです。
要するに、配列名はポインタではありませんが、ほとんどのコンテキストでは、ポインタであるかのように扱われます。
編集する
コメントで質問に答える:
sizeofを使用する場合、配列の要素のみのサイズをカウントしますか?次に、配列の「ヘッド」も長さとポインタに関する情報を使用して領域を占有します(これは、通常のポインタよりも多くの領域を使用することを意味します)?
配列を作成する場合、割り当てられる唯一のスペースは要素自体のスペースです。別のポインターまたはメタデータ用のストレージは実体化されません。与えられた
char a[10];
あなたが覚えているのは
+---+
a: | | a[0]
+---+
| | a[1]
+---+
| | a[2]
+---+
...
+---+
| | a[9]
+---+
発現は、 a
アレイ全体を指すが、全くありません対象 a
配列要素自体とは別のは したがって、sizeof a
配列全体のサイズ(バイト単位)がわかります。式&a
は、配列のアドレスを提供します。これは、最初の要素のアドレスと同じです。差&a
とは、&a[0]
結果のタイプである1 - char (*)[10]
最初のケース及びchar *
第二に。
奇妙なのは、個々の要素にアクセスしたいときです。式a[i]
は、結果として定義され*(a + i)
ます。アドレス値を指定すると、そのアドレスからの要素(バイトではなく)をa
オフセットし、結果を逆参照します。i
問題は、それa
がポインタやアドレスではなく、配列オブジェクト全体にあることです。コンパイラは、(例えば、配列型の発現を見るたびにこのように、C内のルールは、そのa
タイプを有する、char [10]
)及びその発現はのオペランドではないsizeof
か、単項&
演算子、その式のタイプは、(「減衰」)に変換されポインタ型(char *
)に、式の値は配列の最初の要素のアドレスです。したがって、式は a
式と同じ型と値を有し&a[0]
(ひいては、式は*a
式と同じタイプ及び値を有しますa[0]
)。
CはBと呼ばれる以前の言語に由来し、Bにしてa
いた配列要素から別のポインタオブジェクトa[0]
、a[1]
などリッチーは、Bの配列のセマンティクスを維持したいが、彼は別のポインタオブジェクトを格納して台無しにしたくありませんでした。それで彼はそれを取り除きました。代わりに、コンパイラーは必要に応じて、変換中に配列式をポインター式に変換します。
配列にはサイズに関するメタデータは格納されないことを思い出してください。その配列式がポインターに「減衰」するとすぐに、1つの要素へのポインターしかありません。その要素は、一連の要素の最初の要素でも、単一のオブジェクトでもかまいません。ポインタ自体に基づいて知る方法はありません。
配列式を関数に渡すと、関数が受け取るすべての要素は最初の要素へのポインターです-配列の大きさはわかりません(これがgets
関数が非常に脅威であり、最終的にライブラリから削除された理由です)。関数が配列の要素数を知るには、センチネル値(C文字列の0ターミネーターなど)を使用するか、要素数を別のパラメーターとして渡す必要があります。
sizeof
は演算子であり、オペランドのバイト数(オブジェクトを表す式、または括弧内の型名)に評価されます。したがって、配列の場合sizeof
は、要素の数に単一の要素のバイト数を掛けた値に評価されます。場合int
4バイト幅であり、その後の5要素の配列は、int
20バイトを占めます。
[ ]
特別ではありませんか?たとえば、、次にの場合、int a[2][3];
にx = a[1][2];
書き換えることはできますがx = *( *(a+1) + 2 );
、ここでa
はポインタ型に変換されませんint*
(a
関数の引数の場合はに変換する必要がありますint*
)。
a
はtypeがありint [2][3]
、type に「減衰」しint (*)[3]
ます。式に*(a + 1)
はtype int [3]
があり、「減衰」してになりint *
ます。したがって、*(*(a + 1) + 2)
タイプはになりint
ます。 a
第3要素の配列を指しint
、a + 1
の第3要素の配列を指しint
、*(a + 1)
での第3要素の配列int
、*(a + 1) + 2
第二の配列の第三の要素を指しint
、そう*(*(a + 1) + 2)
での第二の配列の第三の要素はint
。それがどのようにマシンコードにマッピングされるかは完全にコンパイラ次第です。
このように宣言された配列
int a[10];
メモリを10 int
秒間割り当てます。変更はできませんa
が、を使用してポインタ演算を実行できますa
。
このようなポインタは、ポインタだけにメモリを割り当てますp
。
int *p;
は割り当てられませんint
。あなたはそれを修正することができます:
p = a;
次のように、配列の添字を使用できます。
p[2] = 5;
a[2] = 5; // same
*(p+2) = 5; // same effect
*(a+2) = 5; // same effect
int
自動ストレージduration`と秒。
配列名だけでメモリ位置が得られるため、配列名をポインタのように扱うことができます。
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
ポインタを。
-std=c11 -pedantic-errors
無効なCコードを書き込むとコンパイラエラーが発生します。のint (*)[3]
変数にを割り当てようとするからですint**
。これは、互いにまったく関係のない2つの型です。したがって、この例が証明するはずのものは、私にはわかりません。
int **
タイプは1つが良く使用する必要があり、そこにポイントではありませんvoid *
。このため。
配列名はポインタのように動作し、配列の最初の要素を指します。例:
int a[]={1,2,3};
printf("%p\n",a); //result is similar to 0x7fff6fe40bc0
printf("%p\n",&a[0]); //result is similar to 0x7fff6fe40bc0
両方の印刷ステートメントは、マシンに対してまったく同じ出力を提供します。私のシステムではそれは与えました:
0x7fff6fe40bc0
配列は、メモリ内の連続した要素のコレクションです。Cでは、配列の名前は最初の要素のインデックスであり、オフセットを適用すると、残りの要素にアクセスできます。「最初の要素へのインデックス」は、実際にはメモリ方向へのポインタです。
ポインター変数との違いは、配列の名前が指している場所を変更できないことです。そのため、constポインターに似ています(似ていますが、同じではありません。Markのコメントを参照してください)。しかし、ポインタ演算を使用する場合、値を取得するために配列名を逆参照する必要がないことも:
char array = "hello wordl";
char* ptr = array;
char c = array[2]; //array[2] holds the character 'l'
char *c1 = ptr[2]; //ptr[2] holds a memory direction that holds the character 'l'
だから答えはちょっと「はい」です。
配列名は、配列の最初の要素のアドレスです。つまり、配列名はconstポインタです。