2

次の2次元配列を検討してください。

int array[2][2] = {
                    {1,2},
                    {3,4}
                  };

私の理解によると:-'array'は、2次元配列のベースアドレスを表します(これは、配列の最初の要素のアドレス、つまりarray [0] [0]と同じです)。

  • メモリ内の2Dアレイの実際の配置は、大きな1Dアレイのみのようなものです。

  • これで、ベースアドレス=配列であることがわかりました。したがって、要素array[0][0]を含むメモリブロックに到達できるはずです。

  • 2次元配列のことを忘れて、この配列を単純な1次元配列として処理しようとすると、array [0] = *(array + 0)は、要素array [0ではなく、最初の配列のベースアドレスを示します。 ][0]。なんで?

  • 2次元配列は、(ポインタの配列のように)メモリアドレスを格納しません。

  • ベースアドレスがわかっている場合は、このメモリに線形1次元配列としてアクセスできる必要があります。

この疑問を明確にするのを手伝ってください。

ありがとう。

4

5 に答える 5

5

「ポインター演算を恐れるな」...

int array[2][2] = { { 1, 2}, { 3, 4 } };
int *ptr = (int *)&array[0][0];
int i;
for (i = 0; i < 4; i++) {
    printf("%d\n", ptr[i]);
}

なぜこれが機能するのですか?C 標準では、多次元配列がメモリ内で連続していることを指定しています。つまり、要素の順序に関して、2D 配列がどのように配置されるかは次のようになります。

array[0][0]
array[0][1]
array[1][0]
array[1][1]

もちろん、配列のアドレスを int へのポインター ( int *、名前を付けましょうptr) として取得すると、アイテムのアドレスは次のようになります。

ptr + 0 = &array[0][0]
ptr + 1 = &array[0][1]
ptr + 2 = &array[1][0]
ptr + 3 = &array[1][1]

そして、それが最終的に機能する理由です。

于 2012-09-05T17:59:55.873 に答える
5

array[0]は一次元配列です。そのアドレスは のアドレスと同じで、 のアドレスarrayと同じですarray[0][0]:

assert((void*)&array == (void*)&(array[0]));
assert((void*)&array == (void*)&(array[0][0]));

は配列であるためarray[0]、変数に割り当てたり、関数に渡したりすることはできません (そうしようとすると、代わりに最初の要素へのポインターが渡されます)。(array[0])[0]andを見ると、それが配列であることがわかります(array[0])[1](括弧は冗長です)。

printf("%d %d\n", (array[0])[0], (array[0])[1]);

そのサイズは 2 つのintオブジェクトのサイズであることがわかります。

printf("%z %z %z\n", sizeof(array), sizeof(array[0]), sizeof(array[0][0]));

メモリ レイアウトを表す図を次に示します。

+-------------+-------------+-------------+-------------+
|      1      |      2      |      3      |      4      |
+-------------+-------------+-------------+-------------+
 `array[0][0]' `array[0][1]' `array[1][0]' `array[1][1]'
`---------array[0]---------' `---------array[1]---------'
`-------------------------array-------------------------'
于 2012-09-05T18:04:00.627 に答える
3
  1. メモリ内の 2 次元配列の実際の配置は、大きな 1 次元配列のみに似ています。

    はい、ストレージ領域は 1D 配列のように連続しています。ただし、インデックスの方法は少し異なります。

    2-D[0][0] = 1-D[0] 
    
    2-D[0][1] = 1-D[1] 
    
    ... 
    
    2-D[i][j] = 1-D[ i * rowsize + j]
    
    ...
    
  2. 2 次元配列のことを忘れて、この配列を単純な 1 次元配列として扱おうとする場合: array[0] = *(array+0) は最初の配列のベース アドレスを示し、要素 array[0 ではありません。 ][0]。なんで?
    *(array+0) は配列へのポインタを意味します。このような形式の最初の要素インデックスは *((*array+0)+0) である必要があります。

    したがって、最終的には *(*array) になるはずです

  3. 2 次元配列には、(ポインターの配列のような) メモリ アドレスは格納されません。もちろん、できます。例えば ​​、

    int * array[3][3] ={ null, };
    
  4. ベース アドレスがわかっている場合、このメモリに線形 1 次元配列としてアクセスできる必要があります。

    この正式な 2-D[i][j] = 1-D[ i * 行サイズ + j]... を使用します。

于 2012-09-05T18:05:06.117 に答える
1

配列はポインターではありません。

ほとんどの場合1、「N 要素配列」型のTは「へのポインタ」型の式に変換 (「減衰」) され、式Tの値は、その最初の要素のアドレスになります。配列。

の型arrayは「2要素配列の2要素配列int」です。上記の規則に従って、これはほとんどの状況で「 int( int (*)[2]) の 2 要素配列へのポインター」に減衰します。これは、式*array(および拡張により、*(array + 0)and array[0]) の型が「 の 2 要素配列int」であることを意味します。タイプに崩壊しint *ます。

したがって、*(array + i)次のi' 番目の2 要素配列がint得られますarray(つまり、 int の最初の 2 要素配列はarray[0]( *(array + 0)) にあり、 の 2 番目の 2 要素配列intarray[1]( ) にあり*(array + 1)ます)。

arrayの 1 次元配列として扱いたい場合はint、次のようにキャスティング体操を行う必要があります。

int *p = (int *) array;
int x = p[0];

また

int x = *((int *) array + 0);


1. 例外は、配列式がsizeofまたは 単項演算子のオペランドである場合&、または宣言で別の配列を初期化するために使用される文字列リテラルである場合です。

于 2012-09-05T18:37:48.743 に答える
0

私はH2CO3の答えが好きです。ただし、配列へのポインタを次のようにインクリメント可能な変数として扱うこともできます。

int array[2][2] = { { 1, 2}, { 3, 4 } };
int *ptr = (int *)array;
int i;
for (i = 0; i < 4; i++) 
{
    printf("%d\n", *ptr);
    ptr++;  
}

++演算子は、ポインターに対して問題なく機能します。ポインタをそのタイプの1つのアドレス、この場合はintのサイズだけインクリメントします。

cの配列には常に注意を払う必要があります。以下は、問題なくコンパイルされます。

int array[2][2] = { { 1, 2}, { 3, 4 } };
int *ptr = (int *)array;
int i;
for (i = 0; i < 100; i++)  //Note the 100
{
    printf("%d\n", *ptr);
    ptr++;  
}

これにより、アレイがオーバーフローします。これに書き込んでいる場合、forループのiやポインタ自体のアドレスなど、プログラム内の他の値が破損する可能性があります。

于 2012-09-05T18:22:44.380 に答える