6.2 怎样定义和引用二维数组
在前面的章节中已经提到,有些问题需要用二维数组来处理。比如说,有3个小分队,每个分队有6名队员,我们需要保存这些队员的工资信息供日后查询。此时,我们就需要用到二维数组,如图 6.6 所示。如果我们创建一个名为 pay
的数组,它应该是一个二维数组,第一维用于表示分队编号,第二维用于表示队员编号。例如,pay[1][2]
表示第二分队第三名队员的工资为 1725。
图 6.6:工资表示例
队员1 队员2 队员3 队员4 队员5 队员6
1分队 2456 1847 1243 1600 2346 2757
2分队 2018 1725 2020 2458 1436
3分队 1427 1175 1046 1976 1477 2018
二维数组通常被称为矩阵(matrix),它以行和列的形式排列,这种结构可以帮助我们形象地理解二维数组的逻辑结构。
6.2.1 怎样定义二维数组
二维数组的定义方式和一维数组相似。例如:
float pay[3][6];
以上代码定义了一个 float
类型的二维数组,第一维表示有 3 个元素,第二维表示有 6 个元素。每一维的长度分别用一对方括号括起来。
二维数组的定义格式如下:
类型说明符 数组名[常量表达式][常量表达式];
类型说明符 数组名[常量表达式][常量表达式];
例如:
float n[3.4], b[5.10]; // 错误的数组定义
这行代码定义了两个数组:a
是 3×4(3 行 4 列)的数组,b
是 5×10(5 行 10 列)的数组。需要注意的是,不能在方括号内写两个下标:
a[0] → a[0][0], a[0][1], a[0][2], a[0][3]
a[1] → a[1][0], a[1][1], a[1][2], a[1][3]
a[2] → a[2][0], a[2][1], a[2][2], a[2][3]
二维数组可以看作特殊的一维数组,其每个元素都是一个一维数组。例如,a
可以看作是一个有 3 个元素的一维数组,每个元素都是一个包含 4 个元素的一维数组,如图 6.7 所示。
图 6.7:二维数组的结构
a[0] → a[0][0], a[0][1], a[0][2], a[0][3]
a[1] → a[1][0], a[1][1], a[1][2], a[1][3]
a[2] → a[2][0], a[2][1], a[2][2], a[2][3]
在 C 语言中,二维数组的元素按行存储。在内存中,首先顺序存放第一行的元素,然后存放第二行的元素,以此类推。图 6.8 和图 6.9 显示了二维数组 a[3][4]
在内存中的存放顺序和位置。
图 6.8:二维数组的排列方式
第 0 行元素
a[0][0], a[0][1], a[0][2], a[0][3]
第 1 行元素
a[1][0], a[1][1], a[1][2], a[1][3]
第 2 行元素
a[2][0], a[2][1], a[2][2], a[2][3]
图 6.9:二维数组在内存中的存放位置
2000 a[0][0]
2004 a[0][1]
2008 a[0][2]
2012 a[0][3]
2016 a[1][0]
2020 a[1][1]
2024 a[1][2]
2028 a[1][3]
2032 a[2][0]
2036 a[2][1]
2040 a[2][2]
2044 a[2][3]
C 语言还允许使用多维数组。有了二维数组的基础,掌握多维数组就不难了。定义三维数组的方法如下:
float a[2][3][4];
这段代码定义了一个三维数组 a
,它有 2 页,3 行,4 列。多维数组的元素在内存中的排列顺序是:第 1 维的下标变化最慢,最右边的下标变化最快。例如,以上三维数组的元素排列顺序如下:
css
Copy code
a[0][0][0] → a[0][0][1] → a[0][0][2] → a[0][0][3] → a[0][1][0] → a[0][1][1] → a[0][1][2] → a[0][1][3] a[0][2][0] → a[0][2][1] → a[0][2][2] → a[0][2][3] → a[1][0][0] → a[1][0][1] → a[1][0][2] → a[1][0][3] a[1][1][0] → a[1][1][1] → a[1][1][2] → a[1][1][3] → a[1][2][0] → a[1][2][1] → a[1][2][2] →
6.2.2 怎样引用二维数组的元素
二维数组中的元素使用数组名和两个下标来表示,格式为:数组名[行下标][列下标]
。例如,a[2][3]
表示数组 a
中,第 2 行第 3 列的元素。下标可以是整型表达式,例如:a[2-1][2*2-1]
。需要注意的是,不要使用逗号来分隔下标,避免像 a[2, 3]
或 a[2-1, 2*2-1]
这样的表示方式。
数组元素既可以出现在表达式中,也可以被赋值。例如:
b[1][2] = a[2][3] / 2;
注意:在引用数组元素时,务必确保下标值在定义的数组范围内,否则会产生数组越界错误。例如:
int a[3][4];
// 定义一个 3×4 的二维数组 a
a[3][4] = 3; // 错误的操作:不存在 a[3][4] 元素
根据定义,数组 a
中“行下标”的有效范围是 0~2,“列下标”的有效范围是 0~3。因此,a[3][4]
超过了数组的有效范围。
提示:请严格区分定义数组时使用的 a[3][4]
和引用数组元素时的 a[3][4]
。在定义数组时,a[3][4]
表示数组的维数和每一维的大小;而引用元素时,a[3][4]
中的 3 和 4 是元素的下标值,表示数组第 4 行第 5 列的元素(数组的行序号和列序号都是从 0 开始的)。
6.2.3 二维数组的初始化
可以使用“初始化列表”来初始化二维数组:
-
按行赋初值:例如:
int a[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
这种方式很直观,每一对花括号中的数据都会被赋值给相应的行。例如,第一个花括号内的数值赋给第 1 行,第二个花括号内的数值赋给第 2 行,以此类推。
-
所有数据在一个花括号内:例如:
int a[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
该方式的效果和上面相同,但建议优先使用第一种方式,因为每一行都有独立的花括号,界限明确。如果所有数据都放在一个花括号内,容易遗漏数据或出错。
-
部分元素赋初值:
只为各行的某些元素赋初值,其他元素自动为 0。例如:
int a[3][4] = {{1}, {5}, {9}};
这只对每行的第 1 列(下标为 0)赋值,其他列会自动初始化为 0。结果如下:
1 0 0 0 5 0 0 0 9 0 0 0
或者只给某行的某些列赋初值,例如:
int a[3][4] = {{1}, {0, 6}, {0, 0, 11}};
初始化后的数组元素为:
1 0 0 0 0 6 0 0 0 0 11 0
这种方法在非 0 元素较少时非常方便,不必为所有的 0 元素都手动赋值。
-
省略第一维长度:
如果为所有元素赋值,则可以省略第一维的长度,但第二维的长度不能省略。例如:
int a[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; int a[][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
这两种方式的效果相同,系统会根据总元素数量和第二维的长度来计算第一维的长度。这里共有 12 个元素,每行 4 列,因此可以确定行数为 3。
如果只对部分元素赋初值且省略第一维长度,仍需按行初始化。例如:
int a[][4] = {{10, 0, 3}, {}, {0, 10}};
这样,系统可以知道数组共有 3 行。
通过这些初始化方法,C 语言的二维数组定义和初始化方式非常灵活。它使用 a[][]
这种两个方括号的方式,让初始化清晰易懂,方便使用且不容易出错。
6.2.4 二维数组程序举例
例 6.4:将一个二维数组的行和列元素互换,存储到另一个二维数组中。
解题思路:定义两个数组:a
为 2 行 3 列,用于存储指定的 6 个数;b
为 3 行 2 列,初始时为空。只需将 a[i][j]
元素存储到 b[j][i]
中。通过嵌套 for
循环完成。
程序代码:
#include <stdio.h>
int main() {
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
int b[3][2], i, j;
// 输出数组 a
printf("Array a:\n");
for (i = 0; i < 2; i++) {
for (j = 0; j < 3; j++) {
printf("%5d", a[i][j]);
b[j][i] = a[i][j]; // 将 a[i][j] 的值赋给 b[j][i]
}
printf("\n");
}
// 输出数组 b
printf("Array b:\n");
for (i = 0; i < 3; i++) {
for (j = 0; j < 2; j++) {
printf("%5d", b[i][j]);
}
printf("\n");
}
return 0;
}
运行结果:
Array a:
1 2 3
4 5 6
Array b:
1 4
2 5
3 6
例 6.5:在一个 3×4 的矩阵中,找出值最大的元素,以及其所在的行号和列号。
解题思路:用“打擂台算法”来找到矩阵中的最大值。首先,让 a[0][0]
作“擂主”,把它赋值给变量 max
,作为当前已知的最大值。然后让下一个元素 a[0][1]
与 max
比较,如果 a[0][1]
比 max
大,则将 a[0][1]
赋给 max
,并记下其所在的行号和列号。循环比较完所有元素,max
就是最大值。
程序代码:
#include <stdio.h>
int main() {
int i, j, row = 0, column = 0, max;
int a[3][4] = {{1, 2, 3, 4}, {9, 8, 7, 6}, {-10, 10, -5, 2}}; // 定义并初始化数组
// 假定 a[0][0] 是最大值
max = a[0][0];
// 遍历二维数组,找出最大值
for (i = 0; i < 3; i++) {
for (j = 0; j < 4; j++) {
if (a[i][j] > max) {
max = a[i][j]; // 更新最大值
row = i; // 记录最大值的行号
column = j; // 记录最大值的列号
}
}
}
// 输出最大值及其所在行号和列号
printf("max = %d\nrow = %d\ncolumn = %d\n", max, row, column);
return 0;
}
运行结果:
max = 10
row = 2
column = 1
最大值是 10,在数组 a
的第 3 行第 2 列,即 a[2][1]。