2016-12-08 8 views
1

C言語の配列は基本的に(sizeof())のようないくつかの場所を除いてポインタのように振る舞います。そのポインタと配列変数を除いて、宣言以外では違いはありません。配列変数のアドレスと内容v C言語のポインタ

たとえば考える2つの宣言:

printf("%d %d", arrptr[0], arr[0]); //11 11 
printf("%d %d", *arrptr, *arr);  //11 11 

しかし、ここで私が見つけたもう一つの場所は、彼らが異なっている:今ここに

int arr[] = {11,22,33}; 
int *arrptr = arr; 

は、彼らが同じように動作する方法です

//the outputs will be different on your machine 
printf("%d %d", &arrptr, &arr); //2686688 2686692 (obviously different address) 
printf("%d %d", arrptr, arr);  //2686692 2686692 (both same) 

ここで最後の行に問題があります。 arrptrにはarrのアドレスが含まれています。最初のアドレスが最後の行に印刷されたのは2686692なのはなぜですか?また、論理的には、とは異なり、arrのアドレス()と内容(arr)が同じである必要があります。しかし、それは正確に何が起こるのか(実装レベルでは内部的に)?

+1

void *にポインタ値をキャストする必要がある唯一の場所でありますポインタや配列については、それと同等のものとして考えるのをやめるのが最善です。それらは構文と使用法が似ている別のものです。理解するのが最も重要なことは、式で配列が使用されるたびに、配列が最初の要素へのポインタに「崩壊する」ということです。したがって、コードでは、最初の要素へのポインタと...最初の要素へのポインタを比較します。もちろん、彼らは同じなので、同じように動作します。 – Lundin

答えて

3

単精度&演算子を配列に適用すると、配列へのポインタが返されます。ポインタに適用すると、ポインタへのポインタを返します。この演算子とsizeofは、配列がでなく、ポインタが崩壊するでないいくつかのコンテキストを表しています。

換言すれば、&arrptrint **であり、&arrint (*)[3]である。 &arrptrはポインタ自体のアドレスであり、&arrは配列の先頭です(arrptrなど)。

微妙な部分:arrptr&arrは同じ値(どちらも配列の先頭を指します)ですが、異なるタイプです。この違いは、ポインタ演算を行う場合に表示されます。arrptrの場合、暗黙のオフセットはsizeof(int)&arrの場合はsizeof(int) * 3となります。

また、void *にキャストした後にポインタを印刷するには、%p形式指定子を使用する必要があります。

2

私はCの配列は、本質的に(はsizeof())のようないくつかの場所以外ではポインタのように振る舞うん知っています。そのポインタと配列変数を除いて、宣言以外では違いはありません。

これは正しくありません。配列の式はほとんどの状況でポインタ式として扱われますが、配列とポインタは完全に異なる動物です。あなたはそれが

+---+ 
a: | | a[0] 
    +---+ 
    | | a[1] 
    +---+ 
    | | a[2] 
    +---+ 
    ... 
    +---+ 
    | | a[N-1] 
    +---+ 

一つのことはすぐに明らかになるにつれて、メモリにレイアウトされています

T a[N]; 

として配列を宣言するとき

- 配列の最初の要素のアドレスが同じです配列自体のアドレス。したがって、&a[0]&aは、同じアドレスの値がになりますが、2つの式のタイプが異なります(T *T (*)[N])。値はタイプに基づいて調整される可能性があります。

物事が少し混乱するかもしれないのはここだ - それはsizeofまたは単項&演算子のオペランドである、または宣言の文字列、タイプの表現「を初期化するために使用される文字列リテラルであるときを除き、N- Tの素子アレイ 『Tへのポインタ」型の表現に「(減衰)』に変換され」、および式の値は、配列の最初のエレメントのアドレスになります。

これはa&a[0]&aと同じアドレス値を与える式を意味し、それは同じタイプ&a[0]としてを有しています。これをまとめて:

Expression  Type  Decays to   Value 
----------  ----  ---------   ----- 
     a  T [N]  T *    Address of a[0] 
     &a  T (*)[N] n/a    Address of a 
     *a  T   n/a    Value of a[0] 
     a[i]  T   n/a    Value of a[i] 
    &a[i]  T *   n/a    Address of a[i] 
    sizeof a  size_t  n/a    Number of bytes in a 

なぜこの変換ルールが最初に存在しますか?

Cは、(図に行く)Bと呼ばれる以前の言語に由来しました。 Bは無形の 言語でした。すべては基本的に符号なし整数として扱われました。メモリ は、固定長の「セル」の線形配列として見られました。あなたはBで 配列を宣言するとき、余分なセルは、アレイの最初 要素にオフセットを格納するために取っておいた。

+---+ 
a:| | ----+ 
    +---+  | 
    ...  | 
    +-------+ 
    | 
    V 
    +---+ 
    | | a[0] 
    +---+ 
    | | a[1] 
    +---+ 
    ... 
    +---+ 
    | | a[N-1] 
    +---+ 

操作a[i]*(a + i)として定義された配列の添字。それは、aに記憶されたオフセット値をとるiを追加し、その結果を逆参照、です。

リッチーがCを設計して、彼はBの配列のセマンティクスを維持したかったが、最初の要素を明示的にポインタをどうするかを把握することができませんでしたので、彼はそれを処分しました。従って、Cが定義a[i] == *(a + i)を添字アレイを保持する(その結果、そのアドレスからと間接参照i要素をオフセット、アドレスaを与えられる)が、配列の最初の要素への別のポインタのためのスペースが確保されない - その代わりに、の配列式aをポインタ値に変換します。あなたはarrarrptrの値を印刷するときに、同じ出力を参照してください理由

です。あなたは%p変換指定子を使用してポインタ値をプリントアウトしてvoid *に引数をキャストする必要があることに注意してください:

printf("arr = %p, arrptr = %p\n", (void *) arr, (void *) arrptr); 

これはかなりあなたが明示的にC.

関連する問題