我知道在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
还是其他方式?或者这取决于什么?
在低级别编程中,并不存在多维数组这种概念。实际上只有一块足够大的内存块来存储指定数量的元素。在C语言中,多维数组的概念是一个元素也是数组的数组。所以如果你这样做:
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]
条目实际占用内存空间,其他所有内容都只是对这些条目之一的引用的一部分)。 可以看到,无论您有多少维,这种模式都将继续。
仅供娱乐:
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]
所有的“维度”在内存中是连续存储的。
考虑以下内容:
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
)是连续的。没错,你说得对-它们是连续存储的。考虑这个例子:
#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
是的,它们只是按连续顺序存储。您可以像这样进行测试:
#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]
,它是一个由3个包含4个包含5个字符的数组组成的数组。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]
类型的(这些是表中的三行)。
arr[x][0] - arr[x][3]
也依次出现,它们中的每个元素都是类型为 char[5]
的(这些是表中每行的四个部分,000-004 是 arr[0][0]
的一个元素)。
arr[x][y][0] - arr[x][y][4]
是 5 个字节,依次出现。array[ny][nx]
,其中ny
和nx
是y和x方向的元素数。此外,这是否意味着我的3D数组应该声明为array[nz][ny][nx]
?a(i,j),1<=i<=M, 1<=j<=N
。因此,您问题中的第一个矩阵是一个3x2矩阵。a[i][j+1]
紧邻a[i][j]
,而a[i+1][j]
则相距N个元素。通常说“最后一个下标变化最快”,或者经常被称为“按行存储”:2D矩阵中的一行(即具有相同第一个索引的元素)在内存中连续放置,而列(具有相同第二个索引的元素)则由相距较远的元素组成。这对于性能考虑很重要:访问邻居元素通常更快(由于硬件缓存等),因此例如嵌套循环应该组织为最内层循环迭代最后一个索引。array[NY][NX]
。但是,如果您需要将真实的2D或3D数据描述为数组,则索引的选择可能取决于其他事情:数据格式,方便的符号,性能等。例如,如果位图的内存表示形式是您需要使用的格式中的array[NX][NY]
,则将以这种方式声明它,甚至可能不需要知道位图变得有点“转置” :)3D数组是扩展的2D数组。
例如,我们有一个数组 - int arr(3)(5)(6);
这是一个由两个2D数组组成的数组,其中每个数组都有4行3列的2D数组。
int a[3][2];
。 - Alexey Kukanovarray[ny][nx]
,其中ny
和nx
是y和x方向上元素的数量。此外,这是否意味着我的3D数组应该声明为array[nz][ny][nx]
? - robintw