3D数组如何存储在C?

我知道C中的数组是按行优先顺序分配的。 因此,对于2 x 3arrays:

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 

或者是其他方式? 还是取决于某些东西?

所有“尺寸”都连续存储在内存中。

考虑

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

你可以说arr[1]arr[2] (types为int[100][20] )是连续的
或者arr[1][42]arr[1][43]int[20]types)是连续的
或者arr[1][42][7]arr[1][42][8]inttypes)是连续的

在低层次上,没有multidimensional array这样的东西。 只有一块平坦的内存,足够容纳一定数量的元素。 在C中,multidimensional array在概念上是一个数组,其元素也是数组。 所以如果你这样做:

 int array[2][3]; 

从概念上讲,你最终得到:

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

这导致元素被连续排列在内存中,因为array[0]array[1]实际上并不包含任何数据,它们只是对两个内部数组的引用。 请注意,这意味着只有[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]条目实际上占用了内存中的空间,其他的只是这些条目之一的引用的一部分)。 正如你所看到的,无论你有多less维度,这个模式都会继续下去。

而只是为了好玩:

 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] 

是的,你是对的 – 他们连续存储。 考虑这个例子:

 #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

我想你已经回答了你自己的问题。 multidimensional array以行优先顺序存储。

请参阅ANSI C规范第3.3.2.1节(也有一个具体示例):

连续的下标运算符指定multidimensional array对象的成员。 如果E是尺寸为ixj“x … x”k的n维数组(n = 2),则E(用作不是左值)转换为指向(n-1)维数组的指针具有尺寸j“x … x”k。 如果一元操作符被明确地应用于这个指针,或者作为下标的结果被隐式地应用,那么结果就是指向(n-1)维的数组,它本身被转换成一个指针,如果它不是一个左值。 由此可见,数组以行优先顺序存储(最后的下标变化最快)。

对于你的例子,你可以试试看看 – http://codepad.org/10ylsgPj

是的,他们只是连续的顺序存储。 你可以这样testing:

 #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)的多索引数组的访问被转换为像这样的一维访问:

 array[i][j][k] = array[M*L*i + L*j + k] 

比方说,你有一个数组char arr[3][4][5] 。 它是由4个5字节的数组组成的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]是一个接一个的,但是每个元素的types都是char[4][5] (这些是表中的三行)。

arr[x][0] - arr[x][3]也是接踵而来的,它们中的每个元素都是char[5]types(这些是表中每行的四个部分,000 – 004是arr[0][0]一个元素)

arr[x][y][0] - arr[x][y][4]是一个接一个的5个字节。

要回答OP对主要问题的评论(这会有些长,所以我决定去回答,而不是评论):

C中的array[ny][nx]应该声明为array[ny][nx] ,其中nynx是y和x方向上元素的数量。 此外,这是否意味着我的3D数组应该被声明为array[nz][ny][nx]

在math中,M×Nmatrix具有M行和N列。 matrix元素的通常表示法是a(i,j), 1<=i<=M, 1<=j<=N 所以你的问题中的第一个matrix是一个3×2的matrix。

实际上它与通常用于例如GUI元素的符号不同。 800×600位图水平(沿X轴)800像素,垂直600像素(沿Y轴)。 如果有人想把它描述成一个matrix,用math符号表示它将是一个600×800matrix(600行,800列)。

现在,C中的multidimensional array以这样的方式存储在存储器中a[i][j+1]紧挨a[i][j]a[i+1][j]则是N个元素。 通常会说“最后一个下标变化最快”,或者经常被称为“按行存储”:在二维matrix中的一行(即具有相同第一个索引的元素)已经连续地放置在内存中,而列(相同的第二索引)由彼此远离的元素组成。 了解性能考虑很重要:访问邻居元素通常要快得多(由于硬件高速caching等),所以例如嵌套循环应该被组织,使得最内层的迭代遍历最后的索引。

回到问题:如果2D数组的心理图像(抽象)是Carthesian坐标系中的点阵,那么可以,你可以把它想象成array[NY][NX] 。但是如果你需要描述真正的二维或三维数据作为一个数组,索引的select可能取决于其他的东西:数据格式,方便的表示法,性能等等。例如,如果位图的内存表示是array[NX][NY]一个你需要使用的格式,你会这样声明,也许你甚至不需要知道位图变成了“转置”:)

三维数组是一个扩展的二维数组。

例如我们有一个数组 – int arr(3)(5)(6);

这是一个由两个2d数组组成的数组,其中数组将有一个2d数组,其中有4行3列。