[英]C, Multidimensional arrays: array whose elements are one-dimensional arrays?
从C编程:现代方法,第269页第2版一书中可以看出这一陈述是否有意义
就像一维数组的名称可以用作指针一样, 任何数组的名称都可以使用,无论它具有多少维度。 但是,需要注意一些事项。 考虑以下数组:
int a[NUM_ROWS][NUM_COLS];
a
不是指向a[0][0];
的指针a[0][0];
相反,它是指向a[0]
的指针。 这使得如果我们看它从C,其视性的观点出发更感a
不作为二维阵列,但作为一维数组,其元素是一维阵列。 当用作指针时,a
具有类型int (*) [NUM_COLS]
(指向长度为NUM_COLS
的整数数组的NUM_COLS
)。
我很困惑,因为当我认为“其元素是一维数组的数组”时,我认为是一个锯齿状数组 ,但这不是这里发生的事情。这更像是一个带指针算术的宏?
这是参考类型系统以及它如何处理多维数组? 任何人都能解释一下吗?
是的,它是有道理的,不,它甚至没有谈论“衣衫褴褛”或“锯齿状”阵列。 就在我们说的时候
int a[NUM_ROWS][NUM_COLS];
我们正在创建的是一个数组a
,它的数组是...其他数组。 你可以这样想:
+---------------------------------------+
| +--------+--------+--------+--------+ |
a: [0]: | | | | | | |
| +--------+--------+--------+--------+ |
+ +
| +--------+--------+--------+--------+ |
[1]: | | | | | | |
| +--------+--------+--------+--------+ |
+ +
| +--------+--------+--------+--------+ |
[2]: | | | | | | |
| +--------+--------+--------+--------+ |
+---------------------------------------+
(此处NUM_COLS
显然为4, NUM_ROWS
为3.)
两个(或更多)维数组100%类似于简单的单维数组 - 您只需要仔细考虑类比。 如果a
是一个数组,那么在需要其值的表达式中提及a
会导致指向数组的第一个元素&a[0]
的指针。 所以给出的二维阵列a
我们谈论, a
的值是&a[0]
是一个指针数组NUM_COLS
整数 。
如果多维数组下标要正常工作,它必须以这种方式工作。 如果我们写a[i][j]
,那就被解释为(a[i])[j]
。 a
像往常一样变成指向数组第一个元素的指针,但是a[i]
相当于*(a + i)
,其中指针算术最终被指向元素的大小缩放 - 这是在引擎盖下,它更像是*(a+ i * sizeof(*a))
。 因此sizeof(*a)
必须是sizeof(int [NUM_COLS])
或NUM_COLS * sizeof(int)
。 这样, a[i]
让你的i
“个子阵,然后j
可以选择单元格之一- int
尺度的细胞-子数组的。
最后一点说明:我曾经俗称“多维数组”,但严格来说,正如许多常客所指出的那样,C没有多维数组; 它只有一维数组,而我们所认为的二维数组实际上就像我们在这里看到的那样,它的元素碰巧是其他一维数组。 (如果C具有真正的多维数组,则下标可能看起来像a[i,j]
而不是a[i][j]
。)
附录:尽管你的指针运算的提及,我的指针运算的提,它认识到, 有没有参与的指针是很重要的a
的定义 。 指针仅在我们试图“取值” a
,或解释a[i]
如何等同于*(a + i)
。
对于涉及指针的数据结构,我们可以对比代码描述的情况
int *a2[NUM_ROWS];
for(i = 0; i < NUM_ROWS; i++)
a2[i] = malloc(NUM_COLS * sizeof(int));
这给了我们一个非常不同的内存布局:
+-----+
a2: | | +--------+--------+--------+--------+
| *------->| | | | |
| | +--------+--------+--------+--------+
+-----+
| | +--------+--------+--------+--------+
| *------->| | | | |
| | +--------+--------+--------+--------+
+-----+
| | +--------+--------+--------+--------+
| *------->| | | | |
| | +--------+--------+--------+--------+
+-----+
这就是通常所谓的“粗糙”或“锯齿状”阵列,因为在这种情况下,所有行显然都不一定是相同的长度。 然而,几乎神奇地,“衣衫褴褛”阵列中的单元也可以使用a2[i][j]
表示法访问。 为了充满活力,我们可以使用
int **a3 = malloc(NUM_ROWS * sizeof(int *));
for(i = 0; i < NUM_ROWS; i++)
a3[i] = malloc(NUM_COLS * sizeof(int));
导致这种内存布局:
+-----+
a3: | |
| * |
| | |
+--|--+
|
|
V
+-----+
| | +--------+--------+--------+--------+
| *------->| | | | |
| | +--------+--------+--------+--------+
+-----+
| | +--------+--------+--------+--------+
| *------->| | | | |
| | +--------+--------+--------+--------+
+-----+
| | +--------+--------+--------+--------+
| *------->| | | | |
| | +--------+--------+--------+--------+
+-----+
a3[i][j]
也在这里工作。
(当然,在构建像a2
和a3
这样的“动态数组”的实际代码中,我们必须检查以确保malloc
没有返回NULL
。)
另一种看待它的方式......
对于任何类型T
,我们创建一个数组作为
T arr[N];
其中T
可以是int
, char
, double
, struct foo
,等等,并读作“ T
N元素数组”。 它也可以是另一种数组类型 。 所以,而不只是int
,假设T
是的M-元素的数组 int
,我们会为写
int arr[N][M];
这读作“ arr
是int
元素的M元素数组的N元素数组”。 这不是一个锯齿状的数组 - 所有“行”都是相同的大小。 但它也不完全是一个二维数组 - 它是一个数组数组。 表达式arr[i]
具有数组类型( int [M]
)。
这个视图也帮助我们找出指向数组类型的指针。 除非它是sizeof
或一元&
运算符的操作数,或者是用于初始化声明中的字符数组的字符串文字,否则将转换类型为“N元素数组T
”( T [N]
)的表达式 (“衰变”)到“指向T
指针”( T *
)类型的表达式。 同样,如果用数组类型int [M]
替换T
,那么你有一个类型为“N元素数组的M元素数组int
”( int [N][M]
)的表达式,它“衰变”为输入“指针的M-元件阵列int
”( int (*)[M]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.