web-dev-qa-db-ja.com

cの配列名は正確には何ですか?

Cでの配列の名前の種類と使用法を理解するのに苦労しています。長い投稿のように思われるかもしれませんが、ご容赦ください。

次のステートメントは、aが_int []_型であると宣言していることを理解しています。つまり整数の配列です。

_int a[30];
_

aも配列の最初の要素を指し、*(a+2)のようなものは有効です。したがって、a整数へのポインタのように見せます。しかし実際には、タイプ_int []_と_int*_は異なります。前者は配列型であり、後者は整数へのポインタです。

また、タイプ_int []_の変数は、関数に渡すときにタイプ_int*_の変数に変換されます。 C配列のように、参照によって渡されます(sizeof演算子を除く)。

これが私をぶら下げるポイントです。次のコードを見てください。

_int main()
{
    int (*p)[3];
    int a[3] = { 5, 4, 6 };

    p = &a;

    printf("a:%d\t&a:%d\n",a,&a);
    printf("%d",*(*p + 2));
}
_

出力:

_a:2686720       &a:2686720
6
_

では、上記のコードはどのように機能しますか? 2つの質問があります:

  1. aと_&a_の値は同じです。どうして?
  2. int (*p)[3];は正確に何をしますか? 配列へのポインタを宣言します、私はこれを知っています。しかし、配列へのポインタ配列の最初の要素へのポインタおよび配列の名前とどのように異なりますか?

誰かが物事を明確にすることができますか?私は多くの混乱の地獄を抱えています。

ポインター変数の値を出力するために_%p_を使用する代わりに、プレースホルダーとして_%d_を使用する必要があることを知っています。整数プレースホルダーを使用すると、切り捨てられたアドレスが出力される可能性があるためです。しかし、私は物事を単純にしておきたいだけです。

28
darxtrix
  1. aと&aの値は同じです。どうすればよいですか?

それらは同じ値ですが、タイプが異なります。配列オブジェクトには要素間のパディング(前または後)がないため、配列のアドレスと配列の最初の要素のアドレスは同じです。

あれは:

(void *) a == (void *) &a
  1. 正確にはint(* p)[3];配列へのポインターを宣言します、私はこれを知っていますが、配列へのポインターは、配列の最初の要素へのポインターおよび配列の名前とどのように異なりますか?

これらは2つの異なるポインタータイプです。たとえば、ポインタ演算を考えてみましょう。

a + 1   /* address of the second element of the array */
&a + 1  /* address one past the last element of the array */

編集:一般的な需要のため、配列の変換に関する情報を以下に追加しました。

3つの例外を除いて、式では、Tの配列型のオブジェクトが、配列の最初の要素を指すTへのポインタ型の値に変換されます。例外は、オブジェクトがsizeofまたは&単項演算子のオペランドである場合、またはオブジェクトが配列を初期化する文字列リテラルである場合です。

たとえば、次のステートメント:

printf("a:%d\t&a:%d\n", a, &a);

実際には次と同等です:

printf("a:%d\t&a:%d\n", &a[0], &a);

また、d変換指定子は、符号付き整数の出力にのみ使用できることに注意してください。ポインタ値を出力するには、p指定子を使用する必要があります(引数はvoid *である必要があります)。したがって、物事を正しく行うには、次を使用します。

printf("a:%p\t&a:%p\n", (void *) a, (void *) &a);

それぞれ:

printf("a:%p\t&a:%p\n", (void *) &a[0], (void *) &a);
16
ouah

他の回答はすでに問題を説明しています。私はそれをいくつかの図で説明しようとしています。これがお役に立てば幸いです。


配列を宣言するとき

int a[3] = {5, 4, 6}  

メモリの配置は次のようになります

enter image description here

今あなたの質問に答えます:

  1. a&aの値は同じです。どうすればよいですか?

aは配列型であり、配列名aは配列aの最初の要素へのポインタになります(減衰後)。つまり、アドレス0x100を指します。 0x100は、メモリブロック(配列a)の開始アドレスでもあることに注意してください。そして、一般的に、最初のバイトのアドレスは変数のアドレスであると言われていますであることを知っておく必要があります。つまり、変数が100バイトの場合、そのアドレスは最初のバイトのアドレスと等しくなります。

&aは、メモリブロック全体のアドレスです。つまり、配列aのアドレスです。図を参照してください。

enter image description here

これで、a&aの両方が異なるタイプであるにもかかわらず同じアドレス値を持つ理由を理解できます。

int (*p)[3];は配列へのポインタを宣言します、私はこれを知っています。しかし、配列へのポインタは、配列の最初の要素へのポインタおよび配列の名前とどのように異なりますか?

上の図を参照してください。配列へのポインタが配列要素へのポインタとどのように異なるかが明確に説明されています。
&apに割り当てると、pは開始アドレス0x100を持つ配列全体を指します。


注:行について

... C配列のように、参照によって渡されます(sizeof関数を除く)。

Cでは、引数は値で渡されます。 Cでは参照による受け渡しはありません。通常の変数が関数に渡されると、その値はコピーされます;対応するパラメーターを変更しても、変数には影響しません。
配列も値で渡されますが、違いは、配列名が最初の要素へのポインターに減衰し、このポインターが関数のパラメーター(ここではポインター値がコピーされる)に割り当てられることです。配列自体はコピーされません。
通常の変数とは対照的に、引数として使用される配列は、配列自体のコピーが作成されないため、変更から保護されません、代わりに最初の要素へのポインタのコピーが作成されます。

この場合、sizeofは関数ではなく、配列名は引数として機能しないことにも注意してください。 sizeof演算子であり、配列名はオペランド。配列名が単項&演算子のオペランドである場合も同様です。

48
haccks
  1. aは、配列の0番目の要素を指すポインターに対応します。一方、&aの場合も同様です。これは、配列の開始アドレスを示すだけです。

なので、a --> pointer pointing to starting element of array a[],it does not know about other element's location.

&a --->address location for storing array a[] which stores first element location,but knows every element's location

同様に、他の要素の場所は(a + 2)、(a + 4)であり、配列の最後までです。

したがって、あなたはそのような結果を得ました。

  1. int(* p)[3]は配列へのポインタです。 int * p [3]だったとしたら、まったく違う意味になります。これは、このコンテキストとはまったく異なるポインタの配列を意味していました。

配列へのポインタは、配列内の他のすべての要素を自動的に処理します。この場合、あなたのは(p)​​です。

一方、配列の最初の要素へのポインタ、つまりaは、配列の最初の要素についてのみ認識します。次の要素にアクセスするには、ポインタの算術方向を手動で指定する必要があります。この場合は、次を参照してください。 aに2を追加して、aから2番目の要素を取得します。 a + 2、aに4を追加することによる3番目の要素、つまりa +4など。 //整数配列なので、2つの違いに注意してください!

2
Am_I_Helpful

aと&aの値は同じです。これは、ずっと前に、配列のアドレスを取得するためにアドレス演算子&on配列を使用する必要があったためですが、現在は必要ありません。最近の配列の名前(この場合はa)は、配列自体のメモリアドレスを表すだけであり、これは&aから取得するものでもあります。これは、コンパイラーが処理する省略形です。

1
steviesama

質問1に答えると、これは設計されたC言語の単なる側面であり、他のほとんどの現代言語とは異なり、C/C++はメモリ内のアドレスを直接操作でき、それを「理解」する機能が組み込まれています。この小さなスペースで私ができるよりもこれをよりよく説明する多くの記事がオンラインにあります。これが1つで、他にもたくさんあると思います: http://www.cprogramming.com/tutorial/c/lesson8.html

1
Jeff D.

から C99標準n1124 6.3.2.1 p3

Sizeof演算子またはunary&演算子のオペランドである場合、または配列を初期化するために使用される文字列リテラルである場合を除き、「型の配列」タイプの式は、「へのポインタ」タイプの式に変換されます。配列オブジェクトの最初の要素を指し、左辺値ではない ''と入力します。配列オブジェクトにレジスタストレージクラスがある場合、動作は定義されていません。

1
ooga