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]
( int
types)是连续的
在低层次上,没有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]
,其中ny
和nx
是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列。