web-dev-qa-db-ja.com

3D配列はどのようにCに保存されますか?

Cの配列は行優先の順序で割り当てられることを理解しています。したがって、2 x 3配列の場合:

0  1
2  3
4  5

としてメモリに保存されます

0 1 2 3 4 5

ただし、2 x 3 x 2の配列がある場合はどうなりますか:

0  1
2  3
4  5

そして

6  7
8  9
10 11

これらはどのようにメモリに保存されますか?次のように連続しています:

0 1 2 3 4 5 6 7 8 9 10 11

それとも他の方法ですか?それとも何かに依存していますか?

38
robintw

すべての「次元」はメモリに連続して保存されます。

検討する

    int arr[4][100][20];

そうとも言える arr[1]およびarr[2](タイプint[100][20])連続している
またはその arr[1][42]およびarr[1][43](タイプint[20])連続している
またはその arr[1][42][7]およびarr[1][42][8](タイプint)は連続している

18
pmg

低レベルでは、多次元配列のようなものはありません。指定された数の要素を保持するのに十分な大きさのメモリのフラットブロックのみがあります。 Cでは、多次元配列は概念的には要素も配列である配列です。その場合:

int array[2][3];

概念的には、次のようになります。

array[0] => [0, 1, 2]
array[1] => [0, 1, 2]

これにより、メモリ内で要素が連続して配置されます。array[0]およびarray[1]は実際にはデータを保持しておらず、2つの内部配列への単なる参照です。これは、[0, 1, 2]エントリは、実際にはメモリ内のスペースを占有します。このパターンを次の次元に拡張すると、次のことがわかります。

int array[2][3][2];

...次のような構造を提供します:

array[0] => [0] => [0, 1]
            [1] => [0, 1]
            [2] => [0, 1]
array[1] => [0] => [0, 1]
            [1] => [0, 1]
            [2] => [0, 1]

メモリ内で要素を連続的に配置し続けます(上記のように、[0, 1]エントリは実際にメモリ内のスペースを占有し、他のすべてはこれらのエントリの1つへの参照の一部にすぎませんご覧のとおり、このパターンは、ディメンションの数に関係なく続きます。

そしてただの楽しみのために:

int array[2][3][2][5];

あなたにあげる:

array[0] => [0] => [0] => [0, 1, 2, 3, 4]
                   [1] => [0, 1, 2, 3, 4]
            [1] => [0] => [0, 1, 2, 3, 4]
                   [1] => [0, 1, 2, 3, 4]
            [2] => [0] => [0, 1, 2, 3, 4]
                   [1] => [0, 1, 2, 3, 4]
array[1] => [0] => [0] => [0, 1, 2, 3, 4]
                   [1] => [0, 1, 2, 3, 4]
            [1] => [0] => [0, 1, 2, 3, 4]
                   [1] => [0, 1, 2, 3, 4]
            [2] => [0] => [0, 1, 2, 3, 4]
                   [1] => [0, 1, 2, 3, 4]
26
aroth

うん、あなたは正しい-それらは連続して保存されます。この例を考えてみましょう:

#include <stdio.h>

int array3d[2][3][2] = {
  {{0, 1}, {2, 3}, {3, 4}},
  {{5, 6}, {7, 8}, {9, 10}}
};

int main()
{
  int i;
  for(i = 0; i < 12; i++) {
    printf("%d ", *((int*)array3d + i));
  }
  printf("\n");
  return 0;
}

出力:

0 1 2 3 3 4 5 6 7 8 9 10

13
sje397

はい、それらは単に連続した順序で保存されています。次のようにテストできます:

#include <stdio.h>

int main (int argc, char const *argv[])
{
  int numbers [2][3][4] = {{{1,2,3,4},{5,6,7,8},{9,10,11,12}}
                          ,{{13,14,15,16},{17,18,19,20},{21,22,23,24}}};

  int i,j,k;

  printf("3D:\n");
  for(i=0;i<2;++i)
    for(j=0;j<3;++j)
      for(k=0;k<4;++k)
        printf("%i ", numbers[i][j][k]);

  printf("\n\n1D:\n");
  for(i=0;i<24;++i)
    printf("%i ", *((int*)numbers+i));

  printf("\n");

  return 0;
}

つまり、次元(N、M、L)のマルチインデックス配列へのアクセスは、次のような1次元アクセスに変換されます。

array[i][j][k] = array[M*L*i + L*j + k]
8
Thies Heidecke

あなたはあなた自身の質問に答えたと思います。多次元配列は、行優先の順序で格納されます。

ANSI C仕様セクション3.3.2.1を参照してください(特定の例もあります)。

連続した添字演算子は、多次元配列オブジェクトのメンバーを指定します。 Eが次元ixj "x ... x" kのn次元配列(n = 2)である場合、E(左辺値以外として使用)は(n -1)次元配列へのポインターに変換されます。次元j "x ... x" kを持ちます。単項*演算子が明示的に、または添え字付けの結果として暗黙的にこのポインターに適用される場合、結果はポイントされた(n -1)次元の配列になります。 。このことから、配列は行優先の順序で格納されます(最後の添え字が最も速く変化します)。

あなたの例については、あなたはそれを試してみることができます- http://codepad.org/10ylsgPj

6
CodeButcher

配列char arr[3][4][5]があるとします。 5文字の4つの配列の3つの配列の配列です。

簡単にするために、arr[x][y][z]の値はxyzであり、arr[1][2][3]には123を格納するとします。

したがって、メモリ内のレイアウトは次のとおりです。

  |  00  01  02  03  04  05  06  07  08  09  10  11  12  13  14  15  16  17  18  19
--+--------------------------------------------------------------------------------   
00| 000 001 002 003 004 010 011 012 013 014 020 021 022 023 024 030 031 032 033 034 
20| 100 101 102 103 104 110 111 112 113 114 120 121 122 123 124 130 131 132 133 134 
40| 200 201 202 203 204 210 211 212 213 214 220 221 222 223 224 230 231 232 233 234

arr[0]arr[1]、およびarr[2]は次々に追加されますが、の各要素はchar[4][5]型です(これらは表の3行です)。

arr[x][0] - arr[x][3]も次々に追加され、それらの各要素はchar[5]型です(これらは表の各行の4つの部分です。000-004はarr[0][0]の1つの要素です。 )

arr[x][y][0] - arr[x][y][4]は5バイトで、次々に送信されます。

3
MByD

主な質問に対するOPのコメントに答えるには(多少長くなるので、コメントではなく答えを出すことにしました):

Cの配列を_array[ny][nx]_として宣言する必要があります。ここで、nyおよびnxはyおよびx方向の要素数です。さらに、それは私の3D配列が_array[nz][ny][nx]_として宣言されるべきであることを意味しますか?

数学では、MxN行列にはM行とN列があります。マトリックス要素の通常の表記はa(i,j), 1<=i<=M, 1<=j<=Nです。したがって、質問の最初のマトリックスは3x2マトリックスです。

確かに、それは一般的に使用される表記とは異なりますGUI要素。 800x600ビットマップには、800ピクセルの水平方向(X軸に沿って)と600ピクセルの垂直方向(Y軸に沿った)があります。マトリックスとして記述したい場合は、数学表記では600x800マトリックス(600行、800列)になります。

現在、Cの多次元配列は、_a[i][j+1]_が_a[i][j]_の隣にあり、_a[i+1][j]_がN要素離れているような方法でメモリに格納されます。通常、「最後の添え字は最も速く変化する」、またはしばしば「行ごとに格納される」と言われます。2次元行列の行(つまり、同じ最初のインデックスを持つ要素)はメモリに連続して配置されます。 )互いに遠く離れた要素で構成されています。パフォーマンスの考慮事項について知っておくことが重要です:隣接要素へのアクセスは通常はるかに高速です(HWキャッシュなどのため)。たとえば、ネストされたループは、最後のインデックスで最も内側のループが繰り返されるように編成する必要があります。

質問に戻ります:2D配列のメンタルピクチャ(抽象化)がカルテシアン座標の格子の場合、はい、Cで_array[NY][NX]_と考えることができます。ただし、実際の2Dを記述する必要がある場合または配列としての3Dデータ、インデックスの選択はおそらく他のものに依存します:データ形式、便利な表記法、パフォーマンスなど。たとえば、ビットマップのメモリ内表現が必要な形式で_array[NX][NY]_である場合使用するには、そのように宣言し、ビットマップが一種の「転置」になることを知る必要さえないかもしれません:)

2
Alexey Kukanov