5

ポインターへのポインターと配列を作成しましたが、ポインターからポインターへのポインターをint介して配列にアクセスしようとすると、いくつかの要素がスキップされ、一度に 2 つの要素が移動します (例: 1 から 3)。これが私のコードです:

int main(void) {
    int c=10;
    int p[5]={2,3,5,6,8};
    int *x;
    int **y;
    x=p;
    y=&p;
    printf("p value is %d and p points to %d",p,&p);
    printf("\n x is %d \n",x[1]);
    printf("\n y is %d \n",y[0]);

    return 0;
}

印刷y[1]すると、3 ではなく 5y[2]が印刷され、8 として印刷されます。理由がわかりません。誰でもこれについて私を助けることができますか? ポインターxは正常に機能しておりx[0]=2、 、x[1]=3、のように正しい要素に沿って移動しますx[5]=5。また、p と &p で同じ値が得られる理由を誰か説明できますか

4

4 に答える 4

5

さて、この質問には回答があり、回答が受け入れられましたが、受け入れられた回答でさえ、元の投稿者が見た奇妙な結果を説明していません: なぜ5 と 8y[1]y[2]印刷するのですか? これが説明です。

元の投稿者: 次のステートメントからどのような結果が得られますか?

printf ("Size of integer: %zu\n", sizeof (int));
printf ("Size of pointer: %zu\n", sizeof (int*));

出力は次のようになります。

Size of integer: 4
Size of pointer: 8

つまり、整数のサイズが 4 バイトでポインターのサイズが 8 バイトの 64 ビット マシンでコンパイルしていると推測しています。その仮定に基づいて、これが起こっていることです。

p配列です。いくつかの例外を除いて、任意の式で使用される場合、配列の名前はその最初の要素へのポインターに "崩壊" します。pしたがって、の値にアクセスするたびに、最初の要素のアドレスが生成されます。

&pポインターへの配列の「崩壊」に関する規則の例外の 1 つです。address-of 演算子を配列の名前に適用すると、配列の最初の要素へのポインターではなく、配列全体へのポインターが返されます。

これが意味することは、p&pは同じ値を持ちますが、意味的には大きく異なるということです。印刷すると同じ値が得られます。

 printf("p value is %p and p points to %p", p, &p);  // use %p and not %d for addresses

pただし、これは同じものを&p指すという意味ではありません。p配列の最初の要素のアドレス、つまり&p[0]. 一方、は5 つの整数の配列全体&pのアドレスです。

したがって、 and を次のように定義するxy、次のようになります。

int* x = p;
int** y = &p;

x配列の最初の要素へのポインターが割り当てられます。y配列全体へのポインタが割り当てられます。これは重要な違いです!

yさらに、宣言方法とそれに割り当てる値の間に不一致があります。&pタイプint (*) [5]です。5 の配列へのポインタintyは、単一の へのポインターへのポインターにすぎませんint。コンパイラは、この不一致に関する警告を表示する必要があります。私は:

Warning: incompatible pointer types assigning to 'int**' from 'int (*) 5'

y[1]この不一致により、との値を出力する際の奇妙な結果が説明されy[2]ます。値に何が起こっているかを見てみましょう。

ご存じのとおり、配列の添字は配列の先頭からのオフセットです。

x[0] == *(x + 0)

したがってx[0]、配列の最初の要素、つまり 2 が得られます。

x[1] == *(x + 1)

しかしx、intへのポインタです。では、追加で実際に何が起こっているのx + 1でしょうか? ポインター演算がどのように機能するかを覚えておいてください。ポインタに整数を追加するということは、実際には、その整数倍の要素のサイズを追加していることを意味します。この場合:

x + 1 == x + (1 * sizeof(int))

sizeof(int)お使いのシステムでは は 4 であるため、 の値は配列x[1]内の次の整数であり、3 です。

では、 を印刷y[0]すると、これはどのように評価されるのでしょうか。

y[0] == *(y + 0)

したがって、 が指すアドレスy、つまりのアドレスにある値pが出力されます。これは の最初の要素なpので、結果は 2 になります。

印刷するとどうなりますy[1]か?

y[1] == *(y + 1)

しかし、何yですか?ですpointer to a pointer to an int。したがって、 に 1 を追加するとy、ポインター演算の動作は、再び 1 * が指している要素の型のサイズを追加することになります。

y + 1 == y + (1 * sizeof (int*))

an のサイズはint*4 バイトではなく 8 バイトです。したがって、1 ずつインクリメントするたびyに、8 バイト、つまり2 つの整数のサイズがインクリメントされます。したがって、その値を逆参照すると、配列内の次の整数ではなく、2 つ離れた整数が取得されます。

より明確に説明するには: 配列が要素 1000 で始まると仮定します。次に、それぞれintが 4 バイトかかるため、次のようになります。

 Address      Element
-----------------------
  1000          2
  1004          3
  1008          5
  1012          6
  1016          8


 p == &p == x == y == 1000
 *x == *y == 2

x に 1 を足すと、1 * が足されます。sizeof(int)つまり、実際には 4 が足されます。*(x + 1)x[1]

しかし、y に 1 を追加すると、1 * が追加されますsizeof(int*)。つまり、実際には 8 が追加されます。したがって、1008 が得られ*(y + 1)、アドレス 1008 または 5 の要素が得られます。

これにより、得られる出力が説明されます。ただし、これは合理的なコーディング方法ではありません。ポインターのサイズが常に 8 バイトになるとは思わないでください。を に割り当てないでint (*) []くださいint**。へのポインターへのポインターを逆参照して、結果intを取得することを期待しないでintください。また、常にコンパイラの警告に注意してください

于 2013-08-25T08:57:27.393 に答える