web-dev-qa-db-ja.com

配列名はポインターですか?

配列の名前はCのポインターですか?そうでない場合、配列の名前とポインター変数の違いは何ですか?

187
user188276

配列は配列であり、ポインターはポインターですが、ほとんどの場合、配列名は変換済みポインターになります。よく使用される用語は、ポインタへの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は配列自体。

231

配列が値として使用される場合、その名前は最初の要素のアドレスを表します。
配列が値として使用されない場合、その名前は配列全体を表します。

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 */
31
pmg

配列型の式(配列名など)がより大きな式に現れ、それが&または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ではawas配列要素a[0]a[1]などとは別のポインターオブジェクト。 Bの配列セマンティクスを維持するために、別のポインターオブジェクトを格納することを台無しにしたくありませんでした。そこで彼はそれを取り除きました。代わりに、コンパイラは、必要に応じて変換中に配列式をポインター式に変換します。

配列はサイズに関するメタデータを保存しないと言ったことを思い出してください。その配列式がポインタに「減衰」するとすぐに、1つの要素へのポインタが得られます。その要素は要素のシーケンスの最初のものでも、単一のオブジェクトでもかまいません。ポインター自体に基づいて知る方法はありません。

配列式を関数に渡すと、関数が受け取るのは最初の要素へのポインタだけです-配列の大きさがわかりません(これがgets関数がこのような脅威であり、最終的に削除された理由です)ライブラリから)。関数が配列の要素数を知るには、センチネル値(C文字列の0ターミネーターなど)を使用するか、要素数を個別のパラメーターとして渡す必要があります。


  1. アドレス値の解釈方法に影響を与える可能性のあるものは、マシンによって異なります。
15
John Bode

このように宣言された配列

int a[10];

10 intsのメモリを割り当てます。 aを変更することはできませんが、aを使用してポインター演算を行うことはできます。

このようなポインターは、ポインターpのみにメモリを割り当てます。

int *p;

intsは割り当てられません。変更できます:

p = a;

そして、次のようにできる限り配列添え字を使用します。

p[2] = 5;
a[2] = 5;    // same
*(p+2) = 5;  // same effect
*(a+2) = 5;  // same effect
5
Grumdrig

配列名自体がメモリの場所を生成するため、配列名をポインタのように扱うことができます。

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]);
4
Michael Buen

この例は問題にいくつかの光を当てると思います。

#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;
}

caは同じメモリを「ポイント」していますが、cポインターのアドレスは取得できますが、aポインターのアドレスは取得できません。

1
Palo