Appearance
第3章 二维及多维数组
🎯 本章学习目标
通过本章的学习,你将能够:
- 理解二维数组概念:把二维数组看作"矩阵"或"表格",掌握行和列的含义
- 正确定义与初始化:掌握二维数组的定义语法和多种初始化方式
- 熟练双重遍历:用双层 for 循环按行/按列遍历二维数组
- 掌握行优先存储:理解二维数组在内存中按行优先排列,能计算元素地址
- 避免下标越界:清楚行列下标的合法范围,识别越界陷阱
- 操作矩阵运算:实现矩阵加法、转置等经典操作
- 计算行列统计:统计每行/每列的和、最大值、最小值等
- 实现杨辉三角:用二维数组递推生成杨辉三角
- 二维数组传参:掌握二维数组作为函数参数的规则(列数不能省略)
- 了解多维数组:知道三维及更高维数组的定义和遍历方法
🗺️ 本章知识图谱
二维及多维数组
├── 基础概念
│ ├── 二维数组 = 矩阵/表格
│ ├── 行下标和列下标(从0开始)
│ └── arr[i][j] 的含义
├── 定义与初始化
│ ├── 定义语法:类型 数组名[行数][列数]
│ ├── 分行初始化(推荐)
│ ├── 整体初始化
│ ├── 部分初始化(其余补0)
│ └── 全局 vs 局部数组初始化
├── 内存布局
│ ├── 行优先存储(像读书)
│ ├── 地址计算公式
│ └── 数组越界问题
├── 遍历方式
│ ├── 按行遍历(外层i内层j)
│ ├── 按列遍历(外层j内层i)
│ └── 对角线遍历
├── 典型应用
│ ├── 矩阵加法/减法
│ ├── 矩阵转置
│ ├── 行列统计(和/最大/最小)
│ ├── 棋盘/迷宫表示
│ └── 杨辉三角(递推)
├── 函数参数
│ ├── 二维数组做形参
│ ├── 列数不能省略
│ ├── 行数可以省略
│ └── 原因:地址计算需要列数
└── 多维数组
├── 三维数组(层+行+列)
├── 三重循环遍历
└── 更高维数组(了解)一、为什么需要二维数组?
一维数组像一排储物柜,只有一个编号(下标)。二维数组则像一栋楼的储物柜——需要**楼层号(行)和房间号(列)**两个坐标才能定位一个格子。
在数学中,二维数组对应矩阵(Matrix):
列0 列1 列2 列3
行0 [ 1 2 3 4 ]
行1 [ 5 6 7 8 ]
行2 [ 9 10 11 12 ]这是一个 3行4列(记作 3×4)的矩阵,共 12 个元素。arr[i][j] 表示第 i 行、第 j 列的元素。
⚠️ 易错点:C++ 中数组下标从 0 开始!一个
arr[3][4]的数组,行下标范围是 0~2,列下标范围是 0~3。访问arr[3][4]属于越界,是考试高频陷阱。
二、二维数组的定义与初始化
2.1 定义二维数组
cpp
// 定义一个3行4列的整型二维数组(局部数组若不显式初始化,
// 其元素的值是 未定义(不确定) 的,通常表现为随机值。)
int arr[3][4];
// 定义时初始化为全0
int arr2[3][4] = {};
// 定义双精度浮点型二维数组:5个学生,每人3门课成绩
double score[5][3];💡 小知识:全局数组默认初始化为 0,局部数组若不显式初始化则值不确定。编程题中建议写
int arr[N][M] = {};确保清零。
2.2 分行初始化(推荐 ✅)
用花括号将每一行括起来,最直观、最不易出错:
cpp
int arr[3][4] = {
{1, 2, 3, 4}, // 第0行
{5, 6, 7, 8}, // 第1行
{9, 10, 11, 12} // 第2行
};如果某行初始化值不足,该行剩余元素自动补 0:
cpp
int arr[3][4] = {
{1, 2}, // 第0行: {1, 2, 0, 0}
{5}, // 第1行: {5, 0, 0, 0}
{} // 第2行: {0, 0, 0, 0}
};2.3 顺序初始化
将所有值写在一个花括号内,按行优先顺序依次填充:
cpp
int arr[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};效果与分行初始化完全相同,但可读性较差,元素较多时不推荐。
2.4 省略行数
定义二维数组时,行数可以省略,但列数必须指定:
cpp
// 正确:省略行数,编译器根据初始值推算为2行(每行4列,8个值 → 2行)
int arr[][4] = {1, 2, 3, 4, 5, 6, 7, 8};
// 正确:分行初始化时也可省略行数
int arr2[][3] = { {1, 2, 3}, {4, 5, 6} }; // 自动推算为2行3列🔥 高频考点:以下写法都是错误的,编译不通过:
int arr[3][];// ❌ 列数不能省略int arr[][];// ❌ 两维都不能省略记忆口诀:"左边可以省,右边不能省"——编译器需要知道每行有多少个元素,才能正确计算内存地址。
2.5 初始化方式对比
| 方式 | 示例 | 可读性 | 适用场景 |
|---|---|---|---|
| 分行初始化 | { {1,2},{3,4} } | ⭐⭐⭐⭐⭐ | 日常首选,清晰直观 |
| 顺序初始化 | {1,2,3,4} | ⭐⭐ | 元素较少时 |
| 省略行数 | int a[][4]={...} | ⭐⭐⭐ | 行数不确定时 |
| 部分初始化 | { {1},{3,4} } | ⭐⭐⭐ | 只需赋部分值,其余自动补0 |
三、内存布局——按行连续存储
3.1 行优先存储
C++ 二维数组在内存中是按行连续存放的。int arr[3][4] 的 12 个元素在内存中一字排开:
地址偏移: 0 1 2 3 4 5 6 7 8 9 10 11
值: 1 2 3 4 5 6 7 8 9 10 11 12
|----- 第0行 -----|----- 第1行 -----|----- 第2行 -----|假设 int 占 4 字节,首元素地址为 base,则:
arr[i][j]的地址 = base + (i × 列数 + j) × sizeof(元素类型)
cpp
// &arr[i][j] = &arr[0][0] + (i * 4 + j) * sizeof(int)📌提醒:公式中的
sizeof(元素类型)在GESP考试(Dev-C++环境)中,int固定为 4 字节,不需要过度深究,重点掌握偏移量(i × 列数 + j)的计算即可。🔥 高频考点:地址偏移量公式中乘的是列数而非行数!例如
arr[3][4]中arr[2][1]的偏移量 = 2×4 + 1 = 9(个 int 的位置)。
3.2 验证连续性
cpp
#include <iostream>
using namespace std;
int main() {
int arr[2][3] = { {1, 2, 3}, {4, 5, 6} };
// 输出每个元素的地址,观察相邻元素地址差
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
cout << "arr[" << i << "][" << j << "] 地址: "
<< &arr[i][j] << " 值: " << arr[i][j] << endl;
}
}
// 相邻元素地址相差4字节(一个int的大小),整体连续
return 0;
}⚠️ 注意:行与行之间没有空隙!
arr[0][2]的下一个内存位置紧挨着arr[1][0]。
四、访问与遍历
4.1 访问元素
cpp
int arr[2][3] = { {10, 20, 30}, {40, 50, 60} };
cout << arr[0][0] << endl; // 输出 10(第0行第0列)
cout << arr[1][2] << endl; // 输出 60(第1行第2列)
arr[0][1] = 99; // 修改第0行第1列的值为994.2 行优先遍历(外层行、内层列)
这是最常用的遍历方式,与内存布局一致,缓存友好、效率更高:
cpp
#include <iostream>
using namespace std;
int main() {
int arr[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// 行优先遍历:外层控制行,内层控制列
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
cout << arr[i][j] << "\t";
}
cout << endl; // 每行结束换行
}
return 0;
}输出:
1 2 3 4
5 6 7 8
9 10 11 124.3 列优先遍历(外层列、内层行)
交换内外层循环即可按列遍历:
cpp
// 列优先遍历:外层控制列,内层控制行
for (int j = 0; j < 4; j++) {
for (int i = 0; i < 3; i++) {
cout << arr[i][j] << " ";
}
cout << endl;
}
// 输出:
// 1 5 9
// 2 6 10
// 3 7 11
// 4 8 12⚠️ 易错点:循环条件中,行的上界是行数,列的上界是列数。
arr[3][4]的遍历范围是i < 3,j < 4,写成i <= 3就会越界!
4.4 两种遍历方式对比
| 对比项 | 行优先遍历 | 列优先遍历 |
|---|---|---|
| 外层循环 | 行 i | 列 j |
| 内层循环 | 列 j | 行 i |
| 访问顺序 | 与内存存储顺序一致 | 跨行跳跃访问 |
| 缓存效率 | ⭐⭐⭐⭐⭐ 高 | ⭐⭐ 较低 |
| 适用场景 | 绝大多数情况 | 需按列统计时(如列求和) |
C++ 二维数组按行连续存储,行优先遍历时内存访问地址是递增的(arr[0][0] → arr[0][1] → arr[0][2] → ...),CPU 缓存可以预取相邻内存块,命中率极高。而列优先遍历时,访问 arr[0][0] 后跳到 arr[1][0],中间跨越了一整行的内存,缓存命中率大幅下降。在数据量较大时,两种遍历方式的性能差距可能达到数倍。
💡 编程建议:除非题目明确要求按列处理,否则一律使用行优先遍历。
五、二维数组作为函数参数
5.1 基本规则
二维数组作为函数参数时,必须指定第二维(列数),行数可以省略并通过参数传递:
cpp
// ✅ 正确:列数4明确指定
void printArray(int arr[][4], int rows) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < 4; j++) {
cout << arr[i][j] << " ";
}
cout << endl;
}
}5.2 完整示例
cpp
#include <iostream>
using namespace std;
// 计算矩阵所有元素之和
// 注意:此函数仅适用于列数为 4 的矩阵。
// 因为 C++ 原生二维数组做参数时,第二维(列数)必须写成编译期常量。
int matrixSum(int arr[][4], int rows) {
int sum = 0;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < 4; j++) {
sum += arr[i][j];
}
}
return sum;
}
// 方阵转置(原地修改)
void transpose(int arr[][3], int n) {
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
// 交换 arr[i][j] 和 arr[j][i]
int temp = arr[i][j];
arr[i][j] = arr[j][i];
arr[j][i] = temp;
}
}
}
int main() {
int a[2][4] = { {1, 2, 3, 4}, {5, 6, 7, 8} };
cout << "矩阵之和: " << matrixSum(a, 2) << endl; // 36
int b[3][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
//此函数仅适用于3×3方阵。
//对于一般 m×n 矩阵的转置,需要定义一个独立的 n×m 结果矩阵,而非原地修改。
transpose(b, 3);
// 转置后:
// 1 4 7
// 2 5 8
// 3 6 9
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
cout << b[i][j] << " ";
}
cout << endl;
}
return 0;
}🔥 高频考点:以下函数声明都是错误的:
void func(int arr[][], int n)// ❌ 必须指定列数void func(int arr[3][])// ❌ 列数不能省略原因:编译器需要通过列数来计算每个元素的地址偏移量
(i * cols + j)。 二维数组作为函数参数时,编译器必须知道列数,因为它决定了内存寻址公式arr[i][j] = 基地址 + (i × 列数 + j) × 元素大小。行数只是用来控制循环边界,不会参与内存寻址,所以可以省略。
5.3 指针与二维数组的关系
二维数组的数组名 arr 本质上是一个指向第一行的指针(类型为 int (*)[4]),因此 arr[i] 等价于 *(arr+i),arr[i][j] 等价于 *(*(arr+i)+j)。这一性质解释了为什么函数参数必须指定列数——编译器需要知道每行的宽度才能正确计算地址偏移。
六、典型应用
6.1 矩阵加法
两个同型矩阵对应位置元素相加:
cpp
#include <iostream>
using namespace std;
int main() {
int a[2][3] = { {1, 2, 3}, {4, 5, 6} };
int b[2][3] = { {6, 5, 4}, {3, 2, 1} };
int c[2][3]; // 存储结果
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
c[i][j] = a[i][j] + b[i][j];
}
}
// 输出结果矩阵
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
cout << c[i][j] << " ";
}
cout << endl;
}
// 输出:
// 7 7 7
// 7 7 7
return 0;
}6.2 矩阵转置
将 m×n 矩阵的行和列互换,得到 n×m 矩阵:
cpp
#include <iostream>
using namespace std;
int main() {
int a[2][3] = { {1, 2, 3}, {4, 5, 6} };
int b[3][2]; // 转置后为3行2列
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
b[j][i] = a[i][j]; // 行列互换赋值
}
}
// 输出转置结果
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 2; j++) {
cout << b[i][j] << " ";
}
cout << endl;
}
// 输出:
// 1 4
// 2 5
// 3 6
return 0;
}⚠️ 易错点:转置时注意原矩阵和目标矩阵的维度交换!
a[m][n]转置到b[n][m],赋值方向是b[j][i] = a[i][j],不要写反。
6.3 统计每行/每列的和
cpp
#include <iostream>
using namespace std;
int main() {
int arr[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// 统计每行的和
for (int i = 0; i < 3; i++) {
int rowSum = 0;
for (int j = 0; j < 4; j++) {
rowSum += arr[i][j];
}
cout << "第" << i << "行的和: " << rowSum << endl;
}
// 输出:
// 第0行的和: 10
// 第1行的和: 26
// 第2行的和: 42
cout << "----------" << endl;
// 统计每列的和
for (int j = 0; j < 4; j++) {
int colSum = 0;
for (int i = 0; i < 3; i++) {
colSum += arr[i][j];
}
cout << "第" << j << "列的和: " << colSum << endl;
}
// 输出:
// 第0列的和: 15
// 第1列的和: 18
// 第2列的和: 21
// 第3列的和: 24
return 0;
}💡 区分技巧:求"每行的和"——外层行、内层列累加;求"每列的和"——外层列、内层行累加。
6.4 棋盘/迷宫问题
用二维数组表示棋盘或迷宫,0 和 1 分别表示不同状态(如 0=通路,1=墙壁),是经典应用场景:
cpp
#include <iostream>
using namespace std;
int main() {
// 5x5的迷宫: 0=通路, 1=墙壁
int maze[5][5] = {
{0, 1, 0, 0, 0},
{0, 1, 0, 1, 0},
{0, 0, 0, 1, 0},
{0, 1, 1, 1, 0},
{0, 0, 0, 0, 0}
};
// 输出迷宫地图
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
if (maze[i][j] == 1)
cout << "█ "; // 墙壁
else
cout << "· "; // 通路
}
cout << endl;
}
return 0;
}6.5 杨辉三角
杨辉三角是 GESP 四级的经典编程题,第 i 行第 j 列的值 = 上一行同列 + 上一行前一列:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1cpp
#include <iostream>
using namespace std;
int main() {
int n;
cout << "请输入杨辉三角的行数: ";
cin >> n;
int yh[20][20] = {}; // 初始化为0
for (int i = 0; i < n; i++) {
yh[i][0] = 1; // 每行第一个数为1
yh[i][i] = 1; // 每行最后一个数为1
// 中间元素 = 上一行同列 + 上一行前一列
for (int j = 1; j < i; j++) {
yh[i][j] = yh[i - 1][j - 1] + yh[i - 1][j];
}
}
// 输出杨辉三角
for (int i = 0; i < n; i++) {
for (int j = 0; j <= i; j++) {
cout << yh[i][j] << " ";
}
cout << endl;
}
return 0;
}⚠️ 递推公式:
yh[i][j] = yh[i-1][j-1] + yh[i-1][j],边界条件是每行首尾为 1。注意:当
n = 0时程序不输出任何内容(符合数学定义);当n = 1时输出1(当前代码已正确处理)。实际编程题中,建议先判断n <= 0的情况并给出提示。
七、三维及多维数组
7.1 三维数组的定义
三维数组可以理解为"二维数组的数组",或一栋楼的储物柜——层、行、列三个坐标:
cpp
// 定义一个2层3行4列的三维数组
int arr[2][3][4];
// 分行初始化
int arr[2][3][4] = {
{ // 第0层
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
},
{ // 第1层
{13, 14, 15, 16},
{17, 18, 19, 20},
{21, 22, 23, 24}
}
};7.2 三维数组的遍历
三重 for 循环,从最外层到最内层依次遍历:
cpp
#include <iostream>
using namespace std;
int main() {
int arr[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 sum = 0;
for (int d = 0; d < 2; d++) { // 层
for (int i = 0; i < 3; i++) { // 行
for (int j = 0; j < 4; j++) { // 列
sum += arr[d][i][j];
}
}
}
cout << "所有元素之和 = " << sum << endl; // 300
return 0;
}💡 规律总结:n 维数组需要 n 重循环遍历。三维数组在 GESP 四级中偶尔出现在选择题中考察遍历和初始化,掌握遍历方法即可。
7.3 更高维数组
C++ 理论上支持任意维度的数组,但实际使用中超过三维就非常罕见了:
cpp
int a4[2][3][4][5]; // 四维数组——语法合法,但可读性和实用性很差⚠️ 考试提示:四级考试以二维数组为主,三维数组多出现在选择题中。更高维数组仅做了解。
八、📝 实战练习
练习题 1(程序阅读题)
题目: 阅读以下程序,输出结果是( )
cpp
#include <iostream>
using namespace std;
int main() {
int a[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
int sum = 0;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == j || i + j == 2)
sum += a[i][j];
}
}
cout << sum << endl;
return 0;
}A. 25
B. 30
C. 35
D. 45
🔑 点击查看答案与解析
答案: A
解析:
程序累加满足 i == j(主对角线)或 i + j == 2(副对角线)的元素:
| 位置 | i, j | 条件 | 值 |
|---|---|---|---|
| a[0][0] | (0,0) | i==j ✓ | 1 |
| a[0][2] | (0,2) | i+j==2 ✓ | 3 |
| a[1][1] | (1,1) | 两个条件都满足(只算一次) | 5 |
| a[2][0] | (2,0) | i+j==2 ✓ | 7 |
| a[2][2] | (2,2) | i==j ✓ | 9 |
⚠️ 注意
a[1][1]同时满足两个条件,但因为是||(逻辑或),只累加一次!a[1][1] 同时满足i==j和i+j==2,但if语句无论几个条件为真都只执行一次sum += a[i][j],因此 5 只被累加一次。
总和 = 1 + 3 + 5 + 7 + 9 = 25,选 A。
📌 提醒:实战编程中,建议明确写成两个独立的 if 判断,或者先累加主对角线,再累加次对角线并单独处理中心点(避免重复累加)。把逻辑写清楚,远比追求代码简洁更重要——这是GESP考试评分的重要标准。
练习题 2(单选题)
题目: 已知 int a[4][6];,且 &a[0][0] = 3000(十进制),假设 int 占 4 字节,则 &a[3][2] 的值是多少?( ) A. 3072
B. 3080
C. 3088
D. 3096
🔑 点击查看答案与解析
答案: B
解析:
使用地址公式:
&a[i][j] = &a[0][0] + (i × 列数 + j) × sizeof(int)本题列数 = 6,i = 3,j = 2:
偏移量 = 3 × 6 + 2 = 20(个int的位置)
&a[3][2] = 3000 + 20 × 4 = 3000 + 80 = 3080选 B。
🔥 再次提醒:公式中乘的是列数(6),不是行数(4)!这是最容易出错的地方。如果误用行数:3×4+2=14,3000+56=3056,就会算错。
练习题 3(判断题)
题目: 有定义 int a[5][10];,则 a[4][9] 是最后一个元素,a[5][10] 是第一个越界元素。( )
🔑 点击查看答案与解析
答案: 正确 ✅
解析:
数组下标从 0 开始:
- 行下标范围:0 ~ 4(共 5 行)
- 列下标范围:0 ~ 9(共 10 列)
a[4][9]是合法的最后一个元素a[5][10]行和列都越界了
💡 记忆技巧:n 个元素,下标范围是 0 ~ n-1。就像一排 5 个座位,编号 0 到 4,坐第 5 号就坐地上了。
练习题 4(单选题)
题目: 将 3 行 4 列的矩阵 a 转置到矩阵 b,以下赋值正确的是( ) A. b[i][j] = a[i][j]
B. b[j][i] = a[i][j]
C. b[i][j] = a[j][i]
D. b[j][i] = a[j][i]
🔑 点击查看答案与解析
答案: B
解析:
矩阵转置的核心是行列互换:原矩阵 a 的第 i 行第 j 列元素,放到目标矩阵 b 的第 j 行第 i 列。
- a 是 3×4,b 应该是 4×3
a[i][j]→b[j][i]- 所以赋值语句是
b[j][i] = a[i][j]
选 B。
🍎 生活比喻:就像把一张照片横过来放,原来的第一行变成了第一列,原来的第一列变成了第一行。
练习题 5(填空题)
题目: 杨辉三角递推公式:yh[i][j] = yh[i-1][j-1] + yh[i-1][j],每行首尾元素为 1。
若要求输出前 5 行杨辉三角,请补全代码中第 ____ 行的正确写法。
cpp
int yh[10][10] = {};
for (int i = 0; i < 5; i++) {
yh[i][0] = 1;
_______________; // 填空
for (int j = 1; j < i; j++) {
yh[i][j] = yh[i-1][j-1] + yh[i-1][j];
}
}🔑 点击查看答案与解析
答案: yh[i][i] = 1
解析:
杨辉三角每行的第一个元素(第0列)和最后一个元素(第i列)都是 1。
yh[i][0] = 1:设置每行第一个元素为 1yh[i][i] = 1:设置每行最后一个元素为 1(第 i 行有 i+1 个元素,最后一个下标是 i)
中间元素 yh[i][j](1 ≤ j < i)由递推公式计算。
🔥 高频考点:杨辉三角的边界条件(首尾为1)和递推公式是编程题常考内容。
练习题 6(单选题)
题目: 有定义 int a[3][4] = {{1, 2}, {3}};,以下说法正确的是( ) A. a[0][2] 的值是 0
B. a[1][1] 的值是 3
C. a[2][0] 的值不确定
D. 编译错误,初始化列表元素不够
🔑 点击查看答案与解析
答案: A
解析:
当二维数组部分初始化时,未显式给出的元素自动初始化为 0。
逐行分析:
- 第 0 行:
{1, 2}→[1, 2, 0, 0](后两个补 0) - 第 1 行:
{3}→[3, 0, 0, 0](后三个补 0) - 第 2 行:没有给出 →
[0, 0, 0, 0](整行补 0)
所以:
- A 正确 ✅:
a[0][2]= 0 - B 错误:
a[1][1]= 0,不是 3 - C 错误:
a[2][0]= 0,不是不确定 - D 错误:部分初始化是合法的,其余自动补 0
练习题 7(单选题)
题目: 以下二维数组作为函数参数的声明中,正确的是( ) A. void func(int a[][], int n, int m)
B. void func(int a[3][], int n)
C. void func(int a[][4], int n)
D. void func(int a[3][4]),调用时只能传 3 行 4 列的数组
🔑 点击查看答案与解析
答案: C
解析:
二维数组作为函数参数时:
- 列数必须指定(编译器需要列数计算地址)
- 行数可以省略(由调用者决定实际行数)
逐一分析:
- A 错误:列数也省略了,编译器不知道每行有多少元素
- B 错误:列数省略了,行数指定反而没意义
- C 正确 ✅:列数指定为 4,行数省略,调用时可以传任意行(只要列数是 4)
- D 错误:行数指定的是"至少能容纳多少行",不是"只能传这么多行"。实际上调用时传 5 行 4 列也能通过编译(虽然可能有问题)
💡 记忆口诀:列不能省,行可以省。就像寄快递,必须告诉快递员每层几个(列数),几层可以现场数。
练习题 8(编程题)
题目: 给定一个 3 行 4 列的整数矩阵,求:
- 所有元素中的最大值
- 最大值所在的行号和列号
输入:12 个整数
输出:最大值 行号 列号
🔑 点击查看答案与解析
参考答案:
cpp
#include <iostream>
using namespace std;
int main() {
int a[3][4];
// 输入矩阵
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
cin >> a[i][j];
}
}
int maxVal = a[0][0]; // 假设第一个元素最大
int maxRow = 0, maxCol = 0;
// 遍历找最大值
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
if (a[i][j] > maxVal) {
maxVal = a[i][j];
maxRow = i;
maxCol = j;
}
}
}
cout << maxVal << " " << maxRow << " " << maxCol << endl;
return 0;
}解析:
这是二维数组的经典题型——查找最大值及其位置。核心思路:
- 假设第一个元素是最大值,记录其值和位置
- 遍历所有元素,如果发现更大的,就更新最大值和位置
- 遍历结束后就得到了全局最大值
🍎 生活比喻:就像选班长,先让第一个人当临时班长,然后每个人都跟现任班长比一比,比班长厉害就替换掉。
九、📊 考点总结表
| 考点 | 重要程度 | 出题形式 | 易错点 |
|---|---|---|---|
| 二维数组定义与初始化 | ⭐⭐⭐⭐ | 选择/判断 | 部分初始化时其余元素补0,不是随机值 |
| 列数不能省略(定义及传参) | ⭐⭐⭐⭐⭐ | 选择(判断正误) | 行数可省略,列数必须显式指定 |
| 双重循环遍历 | ⭐⭐⭐⭐⭐ | 编程题/程序阅读 | 行优先 vs 列优先搞混,内外层写反 |
| 内存布局与地址计算 | ⭐⭐⭐⭐ | 选择/计算 | 地址公式乘列数不是行数,i×cols+j |
| 矩阵加法/转置 | ⭐⭐⭐⭐ | 编程题 | 转置时行列维度交换,b[j][i]=a[i][j] |
| 杨辉三角 | ⭐⭐⭐⭐ | 编程题 | 边界条件:每行首尾为1,中间递推 |
| 行列统计(和/最值) | ⭐⭐⭐⭐⭐ | 编程题 | 每行重置累加器,别忘初始化为0 |
| 数组越界 | ⭐⭐⭐⭐⭐ | 选择(陷阱题) | 下标从0开始,n个元素最大下标是n-1 |
| 三维数组遍历 | ⭐⭐⭐ | 选择/程序阅读 | 三重循环顺序:外层到内层 |
⭐ 必背速记卡
┌─────────────────────────────────────────────┐
│ 二维及多维数组 · 必背速记卡 │
├─────────────────────────────────────────────┤
│ 1. 二维数组 = 行 × 列,像表格/矩阵 │
│ 2. 下标从0开始,arr[n][m] 最大下标 n-1, m-1│
│ 3. 行优先存储:先放完一行,再放下一行 │
│ 4. 地址公式:&a[i][j] = 基址 + (i×列数+j)×大小│
│ 5. 二维数组做参数:列不能省,行可以省 │
│ 6. 部分初始化,未给出的元素自动补0 │
│ 7. 按行遍历:外层i内层j │
│ 8. 按列遍历:外层j内层i │
│ 9. 矩阵转置:b[j][i] = a[i][j] │
│ 10. 杨辉三角:首尾为1,中间 = 左上+正上 │
│ 11. 每行求和:内层循环前重置sum=0 │
│ 12. 找最大值:假设第一个最大,遍历更新 │
│ 13. 三维数组:层+行+列,三重循环 │
│ 14. 越界是UB,一定要检查边界条件 │
│ 15. 全局数组默认0,局部数组要手动初始化 │
└─────────────────────────────────────────────┘