C语言多维数组,以及多维数组中的二维数组
C 语言中的多维数组(multidimensional array)其实就是元素为数组的数组。n 维数组的元素是 n-1 维数组。例如,二维数组的每个元素都是一维数组,一维数组的元素当然就不是数组了。
多维数组声明时,每个维度用一对方括号来表示:
数组 screen 包含 10 个元素,从 screen[0] 到 screen[9]。每个元素又是一个二维数组,它有 40 个元素,这 40 个元素均是一维数组,然后每个一维数组内都有 80 个字符。整体来说,screen 数组有 32000(10×40×80)个 char 类型元素。
想要获取该三维数组 screen 内的某个 char 元素,必须指定 3 个索引值。例如,下面的语句把字符Z写入该数组的最后一个元素位置:
float mat[3][5];
这三个元素 mat[0]、mat[1] 和 mat[2] 是矩阵 mat 的三行。每行都是由五个 float 元素所组成的数组。因此,该矩阵包含 3×5=15 个 float 元素,如下表所示:
上图中所指定的值可以利用嵌套循环语句赋值得到的。第一个索引值指定行,第二个索引值定位到该行中的某列:
在内存中,这三行被连续存储在一起,因为它们都是 mat 数组的元素。这样的话,该数组中的 float 值以递增的顺序存储在一起。
如果前面例子的数组 mat 有外部链接(例如,它的定义在所有函数外部),那么其他源代码文件只要做如下声明,就可以使用 mat:
为展示各种可能性,我们假定一个数组定义和初始化如下:
这个初始化列表包含了三个层次的列表大括号,并且用下面的值初始化二维数组 a3d[0] 和 a3d[l] 的元素:
因为所有只要没与初始化器的元素,就会被初始化为默认的 0,所以下面的定义具有一样的效果:
该初始化列表也显示出三个层次的大括号。不需要指定第一个维度为 2,因为最外面的初始化列表包含两个初始化器。
也可以省略某些大括号。如果某对大括号包含了比对应数组维度中元素数量还多的初始化器,那么多出来的初始化器会被关联到存储序列中的下一个数组元素中。因此,下面这两个定义是等价的:
最后,可以利用元素指示符达到相同的初始化目的,如下所示:
上述定义等同于:
如果只有一小部分的元素需要被初始化为 0 以外的值,那么使用元素指示符是一个不错的做法。
多维数组声明时,每个维度用一对方括号来表示:
char screen[10][40][80]; // 一个三维数组
数组 screen 包含 10 个元素,从 screen[0] 到 screen[9]。每个元素又是一个二维数组,它有 40 个元素,这 40 个元素均是一维数组,然后每个一维数组内都有 80 个字符。整体来说,screen 数组有 32000(10×40×80)个 char 类型元素。
想要获取该三维数组 screen 内的某个 char 元素,必须指定 3 个索引值。例如,下面的语句把字符Z写入该数组的最后一个元素位置:
screen[9][39][79] = 'Z';
二维数组(矩阵)
二维数组常常被称为矩阵(matrix)。它应用频繁,因此我们来更详细地讨论下矩阵。把矩阵想成列和行的排列方式,更有助于理解它。下面定义的矩阵 mat 有三行和五列:float mat[3][5];
这三个元素 mat[0]、mat[1] 和 mat[2] 是矩阵 mat 的三行。每行都是由五个 float 元素所组成的数组。因此,该矩阵包含 3×5=15 个 float 元素,如下表所示:
[0] | [1] | [2] | [3] | [4] | |
mat[0] | 0.0 | 0.1 | 0.2 | 0.3 | 0.4 |
mat[1] | 1.0 | 1.1 | 1.2 | 1.3 | 1.4 |
mat[2] | 2.0 | 2.1 | 2.2 | 2.3 | 2.4 |
上图中所指定的值可以利用嵌套循环语句赋值得到的。第一个索引值指定行,第二个索引值定位到该行中的某列:
for ( int row = 0; row < 3; ++row ) for ( int col = 0; col < 5; ++col ) mat[row][col] = row + (float)col/10;
在内存中,这三行被连续存储在一起,因为它们都是 mat 数组的元素。这样的话,该数组中的 float 值以递增的顺序存储在一起。
声明多维数组
在数组声明中,如果执行定义操作的话,数组类型可以是不完整的。也就是说,可以声明数组,却不指定其长度。这种声明所引用的数组,必须在程序其他地方指定它的长度。然而,必须声明一个数组元素的完整类型。对于一个多维数组的声明而言,只有第一个维度可以不指定长度,所有其他维度都必须指定长度。例如,在声明二维数组时,必须要指定列的数量。如果前面例子的数组 mat 有外部链接(例如,它的定义在所有函数外部),那么其他源代码文件只要做如下声明,就可以使用 mat:
extern float mat[ ][5]; // 外部声明
初始化多维数组
可以利用初始化列表来初始化多维数组,还有一些需要特别注意的地方:不必为每个维度都写一对大括号,可以使用多维元素指示符。为展示各种可能性,我们假定一个数组定义和初始化如下:
int a3d[2][2][3] = { { { 1, 0, 0 }, { 4, 0, 0 } }, { { 7, 8, 0 }, { 0, 0, 0 } } };
这个初始化列表包含了三个层次的列表大括号,并且用下面的值初始化二维数组 a3d[0] 和 a3d[l] 的元素:
[0] | [1] | [2] | |
a3d[0][0] | 1 | 0 | 0 |
a3d[0][1] | 4 | 0 | 0 |
[0] | [1] | [2] | |
a3d[1][0] | 7 | 8 | 0 |
a3d[1][1] | 0 | 0 | 0 |
因为所有只要没与初始化器的元素,就会被初始化为默认的 0,所以下面的定义具有一样的效果:
int a3d[ ][2][3] = { { { 1 }, { 4 } }, { { 7, 8 } } };
该初始化列表也显示出三个层次的大括号。不需要指定第一个维度为 2,因为最外面的初始化列表包含两个初始化器。
也可以省略某些大括号。如果某对大括号包含了比对应数组维度中元素数量还多的初始化器,那么多出来的初始化器会被关联到存储序列中的下一个数组元素中。因此,下面这两个定义是等价的:
int a3d[2][2][3] = { { 1, 0, 0, 4 }, { 7, 8 } }; int a3d[2][2][3] = { 1, 0, 0, 4, 0, 0, 7, 8 };
最后,可以利用元素指示符达到相同的初始化目的,如下所示:
int a3d[2][2][3] = { 1, [0][1][0]=4, [1][0][0]=7, 8 };
上述定义等同于:
int a3d[2][2][3] = { {1}, [0][1]={4}, [1][0]={7, 8} };
如果只有一小部分的元素需要被初始化为 0 以外的值,那么使用元素指示符是一个不错的做法。