検索

ホームページ  >  に質問  >  本文

objective-c - 关于C语言指针的问题

C语言指针问题 下面代码为什么第二个数字是5呢?

int a[5] = {1,2,3,4,5};
int *p = (int *)(&a+1);
NSLog(@"%d,%d", *(a+1), *(p-1));
黄舟黄舟2769日前431

全員に返信(3)返信します

  • 阿神

    阿神2017-04-21 11:19:34

    &aは配列へのポインタとして出てくるので、&a+1は実際には配列の長さに基づいて移動します。配列の 2 番目の要素だけを取得したい場合は、*(&a[0]+1) を使用します。これは、&a[0] のデータ型が int* であるためです。最初に絵を描きます。

    次の配列があるとします

    リーリー

    上記のプログラムはコンパイルして渡すことはできますが、x と a の型が一致しないため、コンパイラーは警告を出します

    警告を解除するには 2 つの方法があります。

    1. 強制型変換による

    リーリー

    2. 適切な型を見つけます。&a の型は int (*)[5]

    です。 リーリー

    これには、適切なデータ型の書き方が含まれます。これは、代入とパラメーターの受け渡しにおいて非常に重要です。

    それでは、まず a、&a、&a[0] のデータ型を要約する必要があります

    1. a は配列の名前であり、配列の最初の要素へのポインターです ここで、配列の最初の要素のデータ型は int であるため、a のデータ型は間違いありません。は int* です。

    2. &a は 1 次元配列のアドレスを受け取り、その結果は配列へのポインタ (この場合、int の配列へのポインタ)、つまり int(*)[5] になります。

    3. &a[0] は非常に単純です。まず、a[0] は整数を取得し、そのアドレスを取得するため、そのデータ型は int* です。

    データ型がわかったので、ポインターの演算がより明確になります。

    まず、強制的な型変換を通過するコードの部分を見てみましょう。出力される数値は何でしょうか?

    答えは5です! 先ほどのデータ型の概要から、&a のデータ型は int (*)[5] であることがわかります。そのため、&a+1 は実際には 5*sizeof(int) バイト移動されたことになります。 これで、ポインタは配列の最後の要素の後の要素を指します (図 1)。つまり、境界を越えています。ただし、x のデータ型は実際には int * であるため、 したがって、x-1 の場合、実際には 1*sizeof(int) バイト左に移動します。これは、最後の要素を指すことを意味するため、*(x-1) が取得されます。 値は配列の最後の要素です: 5

    それでは、2 番目の部分を見てみましょう。どのような数値が出力されるでしょうか。答えについては先に述べたのでやめておきます。 データ型は、 sizeof(int)*5 バイト単位で移動するので、実際には x-1 が sizeof(int)*5 バイトだけ左に移動します。 20 バイト、つまり配列の最初の要素に戻ることを意味するため、答えは次のようになります: 1 上記は 1 次元配列ですが、次に 2 次元配列の場合について説明します。

    リーリー

    まず、a、a[0]、&a、&a[0]、&a[0][0] のデータ型を見てみましょう:

    上記の太字のテキストに注目してください。配列名は配列の最初の要素へのポインターです!

    1. a のデータ型は何ですか? ? int *?いいえ、この太字のテキストの核心は 5 つの単語最初の要素です。 a の最初の要素は単なる a[0][0] ではないでしょうか?厳密に言えば、いいえ、a の最初の要素は実際には a[0] です。では、a[0] のデータ型は何でしょうか? [0] は 3 つの int 要素を含む配列であるため、最初の要素のデータ型は であるため、a[0] の型は int t[3] の t の型と同じになります。 int* は、この 2 次元配列を 1 次元配列とみなした場合、実際には 3 つの int* 要素を含む配列になります。 配列はポインターの配列であるため、 a のデータ型は int (*)[3] です。

    2. もう一度 &a を見てみましょう。1 次元配列の説明で述べたように、配列名から取得されるアドレスは配列へのポインターなので、&a のデータ型は
    3. int (*)[3][3] です。

    4. &a[0] これは少し面倒に思えますが、前の段落で述べたように、a[0] は 3 つの int 要素を含む配列であり、
    5. int t[3]

      t のデータ型は同じです (int*)。当然、&a[0] のデータ型は &t のデータ型、つまり int (*)[3] です。

    6. この時点で、2 次元配列のデータ型についてほぼ記述しました。データ型がわかったので、計算後にポインターがどこに移動するかを正確に知ることができます。

    以下のコードを見てください:

    リーリー

      x のデータ型は
    • int (*)[3]

      であるため、図に示すように、x+1 は実際には 3*sizeof(int) バイトだけ移動されます。

    • k のデータ型は
    • int *

      なので、k+1 は実際には sizeof(int) バイトだけ移動されます。

    • yのデータ型は
    • intです (*)[3][3]

      なので、y+1 は実際には 3*3*sizeof(int) バイトだけ移動され、これは配列の最後のバイトに移動します。 要素の後の要素。図に示すように:

    • q のデータ型は
    • int (*)[3]

      なので、q+1 は実際には 3*sizeof(int) バイトだけ移動します。

    • z のデータ型は
    • int *

      なので、z+1 は実際には sizeof(int) バイトだけ移動されます。

    • したがって、配列ポインターを加算および減算するときに最も重要なことは、そのステップ サイズを知ることです。ステップ サイズはデータ型によって決まります。

    返事
    0
  • PHP中文网

    PHP中文网2017-04-21 11:19:34

    一階の回答者さんがとても良く書いてますので、拡大してください

    ポインタの値は配列のアドレスです
    int (* pa)[5];
    pa++;pa は 20 バイト戻ります (5*4)

    対照的に、配列内の要素はポインター (int*) です
    int *pa[5];
    pa++;pa は 4 バイト戻ります

    返事
    0
  • 高洛峰

    高洛峰2017-04-21 11:19:34

    古い投稿を再投稿して申し訳ありません。私がこれを偶然見たのは、過去に組み込みプログラミングを行っていたとき、ポインタ変数にメモリ アドレスを直接割り当てるなど、配列とポインタが頻繁に使用されていたためです (これは通常のアプリケーションでは通常使用されません)。&arr を見つけたことがなかったからです。前にarrとの違いは何ですか?今日具体的に確認したところ、これはコンパイラに依存することがわかりました。一部のコンパイラは、配列とポインタを別々に扱います。配列名のアドレスを取得することは、配列を特定の形式のポインタとみなします。この場合、ポインタは 1 つだけジャンプします。配列要素のメモリですが、配列全体のメモリというものは存在しません。

    返事
    0
  • キャンセル返事