4 一维数组与二维数组
正在加载视频播放器…
GESP C++ 四级:一维数组与二维数组系统梳理
核心目标:深入掌握一维数组的定义、初始化、遍历与常见算法,系统掌握二维数组的定义、初始化、遍历、矩阵操作与函数传参,能熟练解决矩阵转置、边界处理、行列统计等综合问题。
一维数组是“一排连续的同类型存储单元”,二维数组是“一维数组的数组”;下标从0开始,遍历用嵌套循环,矩阵操作注意行列关系和边界条件。
一、考纲定位
GESP 四级考纲对数组的要求包括:
- 掌握一维数组的基本概念及使用,能熟练进行遍历、求和、求最值、查找等操作。
- 掌握二维及多维数组的基本概念及使用,理解二维数组的行列结构,能进行矩阵的遍历、转置、统计等操作。
- 理解数组作为函数参数的传递方式(数组名退化为指针)。
- 四级不考动态内存分配,二维数组的列数必须在编译期确定。
二、一维数组核心回顾
2.1 定义与初始化
int arr[10]; // 定义,未初始化
int arr2[5] = {1, 2, 3, 4, 5}; // 完全初始化
int arr3[5] = {1, 2}; // 部分初始化,其余补0
int arr4[] = {1, 2, 3}; // 自动推导长度
int arr5[100] = {0}; // 全部清零(最常用)
关键规则:
- 数组长度必须是编译期常量,不能用变量定义(四级仍不考变长数组)。
- 下标从 0 开始,长度为 n 的数组有效下标是 0 ~ n-1。
- 数组越界不会产生编译错误,但会导致未定义行为。
2.2 遍历与常见操作
// 标准遍历
for (int i = 0; i < n; i++) { ... }
// 逆序遍历
for (int i = n - 1; i >= 0; i--) { ... }
// 求和
int sum = 0;
for (int i = 0; i < n; i++) sum += arr[i];
// 求最值
int maxv = arr[0];
for (int i = 1; i < n; i++) if (arr[i] > maxv) maxv = arr[i];
// 查找
int pos = -1;
for (int i = 0; i < n; i++) {
if (arr[i] == target) { pos = i; break; }
}
// 逆序
for (int i = 0, j = n - 1; i < j; i++, j--) {
swap(arr[i], arr[j]);
}
2.3 数组与指针(四级重点)
数组名是首地址,是常量指针,不能重新赋值。
int arr[5] = {10, 20, 30, 40, 50};
int *p = arr; // p 指向 arr[0]
cout << *(p + 2); // 30,等价于 arr[2]
cout << p[3]; // 40,等价于 arr[3]
数组作函数参数时退化为指针,函数内 sizeof(arr) 得到的是指针大小,不是数组总大小:
void f(int arr[]) {
cout << sizeof(arr); // 8(64位系统),不是数组总字节数
}
int main() {
int a[10];
cout << sizeof(a); // 40
f(a); // 输出 8
}
结论:数组作为参数时,必须额外传递长度 n。
三、二维数组核心语法
3.1 定义与内存布局
数据类型 数组名[行数][列数];
int matrix[3][4]; // 3行4列
double scores[5][3]; // 5行3列
char grid[10][10]; // 10行10列
内存布局:二维数组在内存中是按行连续存储的。matrix[3][4] 相当于 3 个长度为 4 的一维数组依次排列。
内存布局(按行连续):
matrix[0][0], matrix[0][1], matrix[0][2], matrix[0][3],
matrix[1][0], matrix[1][1], matrix[1][2], matrix[1][3],
matrix[2][0], matrix[2][1], matrix[2][2], matrix[2][3]
3.2 初始化
// 方式一:分行初始化(推荐,清晰直观)
int a[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// 方式二:连续初始化(按行填充)
int b[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
// 方式三:部分初始化,其余补0
int c[3][4] = {{1}, {2}, {3}}; // 每行只初始化第一个元素
// 方式四:全部清零
int d[3][4] = {0};
// 方式五:省略行数,列数必须写
int e[][4] = {{1,2,3,4}, {5,6,7,8}}; // 自动推导为2行
int f[][4] = {1,2,3,4,5,6,7,8}; // 自动推导为2行
关键规则:定义二维数组时,列数必须写,行数可以省略(由初始化列表推导)。
3.3 遍历
标准双重循环:
int n = 3, m = 4;
for (int i = 0; i < n; i++) { // 外层控制行
for (int j = 0; j < m; j++) { // 内层控制列
cout << a[i][j] << " ";
}
cout << endl; // 每行结束换行
}
按列遍历:
for (int j = 0; j < m; j++) {
for (int i = 0; i < n; i++) {
cout << a[i][j] << " ";
}
cout << endl;
}
3.4 二维数组与函数参数
二维数组作为函数参数时,列数必须在编译期可知。
// 方式一:省略首维长度(最常见)
void printMatrix(int a[][4], int rows) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < 4; j++) {
cout << a[i][j] << " ";
}
cout << endl;
}
}
// 方式二:固定两维
void printMatrix(int a[3][4]) { ... }
// 方式三:指针+手算下标
void printMatrix(int *a, int rows, int cols) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
cout << a[i * cols + j] << " ";
}
cout << endl;
}
}
真题示例:以下哪个函数声明在调用时可以传递二维数组的名字作为参数?
答案:void BubbleSort(int a[3][4]);
四、常见矩阵操作
4.1 矩阵转置
将矩阵的行列互换:b[j][i] = a[i][j]。
int a[3][4] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};
int b[4][3];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
b[j][i] = a[i][j];
}
}
// b 变为 4行3列
4.2 矩阵加法
int c[3][4];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
c[i][j] = a[i][j] + b[i][j];
}
}
4.3 矩阵乘法
// a: n×m, b: m×p, c: n×p
int c[3][4] = {0};
for (int i = 0; i < n; i++) {
for (int j = 0; j < p; j++) {
for (int k = 0; k < m; k++) {
c[i][j] += a[i][k] * b[k][j];
}
}
}
4.4 对角线元素
// 主对角线(左上到右下)
for (int i = 0; i < n; i++) {
cout << a[i][i] << " ";
}
// 副对角线(右上到左下)
for (int i = 0; i < n; i++) {
cout << a[i][n - 1 - i] << " ";
}
4.5 矩阵边界处理
// 判断是否在边界
bool isEdge(int i, int j, int n, int m) {
return i == 0 || i == n - 1 || j == 0 || j == m - 1;
}
4.6 行列统计
// 每行之和
for (int i = 0; i < n; i++) {
int rowSum = 0;
for (int j = 0; j < m; j++) {
rowSum += a[i][j];
}
cout << "第" << i << "行之和:" << rowSum << endl;
}
// 每列之和
for (int j = 0; j < m; j++) {
int colSum = 0;
for (int i = 0; i < n; i++) {
colSum += a[i][j];
}
cout << "第" << j << "列之和:" << colSum << endl;
}
五、一维数组与二维数组的对比
| 对比项 | 一维数组 | 二维数组 |
|---|---|---|
| 定义 | int arr[10]; |
int arr[3][4]; |
| 内存布局 | 连续一排 | 按行连续 |
| 遍历 | 单层循环 | 双重循环 |
| 函数参数 | int arr[] + 长度 |
int arr[][列数] + 行数 |
| 常见操作 | 求和、最值、查找、逆序 | 转置、加法、乘法、对角线 |
| 典型应用 | 数列处理、统计 | 矩阵运算、图像处理、棋盘 |
六、GESP四级数组相关真题示例
真题1:二维数组参数声明
以下哪个函数声明在调用时可以传递二维数组的名字作为参数?
A)void BubbleSort(int a[][], int n, int m);
B)void BubbleSort(int a[3][4]);
C)void BubbleSort(int *a, int n);
D)void BubbleSort(int a[][], int n);
答案:B
解析:二维数组作参数时,列数必须在编译期确定,所以int a[3][4]是正确的。
真题2:数组名与指针
以下代码的输出是( )。
int a[3] = {1, 2, 3}; int *p = a; cout << *(p + 2);答案:3
解析:*(p + 2)等价于a[2],值为 3。
真题3:数组作函数参数
以下程序输出是( )。
void f(int arr[]) { cout << sizeof(arr) << endl; } int main() { int a[10]; f(a); }答案:8(64位系统,指针大小)
解析:数组作参数退化为指针,sizeof(arr)得到指针大小。
真题4:指针与数组地址计算
假设整数数组
arr[4] = {0, 1, 2, 3};的第一个元素在内存中的地址为0x7ffee4065820,经过int* p = arr; p += 1;后,指针p的值是( )。
答案:0x7ffee4065824(假设 int 占 4 字节)
七、易错点汇总
7.1 一维数组易错点
- 数组越界:访问
arr[n]或负下标,不会编译错误,但会导致未定义行为。 - 数组名不能赋值:
arr = p;错误,数组名是常量。 - 数组长度必须是常量:
int n; int arr[n];不合规。 - 函数内求数组长度错误:数组参数退化为指针,必须额外传长度。
- 最值初值取0:数组全为负数时结果错误,应取
arr[0]或足够小的数。
7.2 二维数组易错点
- 列数必须写:
int a[][]错误,int a[][4]正确。 - 行列混淆:
a[i][j]中i是行,j是列,遍历时外层行内层列。 - 函数参数列数必须编译期可知:
void f(int a[][], int m)错误。 - 内存布局理解错误:二维数组按行连续,
a[i][j]的地址是基地址 + (i * 列数 + j) * sizeof(类型)。 - 对角线条件错误:主对角线
i == j,副对角线i + j == n - 1(n为阶数)。 - 矩阵乘法维度不匹配:
n×m矩阵乘以m×p矩阵,结果是n×p。
八、速查表
| 需求 | 写法 | 说明 |
|---|---|---|
| 定义一维数组 | int arr[10]; |
长度必须常量 |
| 一维数组清零 | int arr[10] = {0}; |
全部为0 |
| 一维遍历 | for (i=0; i<n; i++) |
下标0~n-1 |
| 定义二维数组 | int a[3][4]; |
3行4列 |
| 二维数组清零 | int a[3][4] = {0}; |
全部为0 |
| 二维遍历 | 外层行,内层列 | i行j列 |
| 二维数组作参数 | void f(int a[][4], int rows) |
列数必须写 |
| 矩阵转置 | b[j][i] = a[i][j]; |
行列互换 |
| 主对角线 | a[i][i] |
左上到右下 |
| 副对角线 | a[i][n-1-i] |
右上到左下 |
| 行和 | 固定i,累加j |
每行求和 |
| 列和 | 固定j,累加i |
每列求和 |
| 矩阵乘法 | 三重循环 | c[i][j] += a[i][k]*b[k][j] |
九、总结
一维数组与二维数组是GESP四级核心数据结构。
- 一维数组是连续的同类型存储单元,下标从0开始。
- 二维数组是“一维数组的数组”,内存按行连续存储。
- 定义二维数组时,列数必须写,行数可省略。
- 遍历二维数组用双重循环:外层控制行,内层控制列。
- 二维数组作函数参数时,列数必须在编译期可知。
- 数组名是首地址,作参数时退化为指针,必须额外传长度。
- 矩阵转置:
b[j][i] = a[i][j]。 - 主对角线
i == j,副对角线i + j == n - 1(n为阶数)。 - 数组越界不会编译错误,但会导致未定义行为,务必检查边界。
- 多练“矩阵转置”“行列统计”“数组作参数”三类题。
复习建议:先把一维数组的求和、最值、查找、逆序四个模板写熟,再系统掌握二维数组的定义、初始化、遍历、矩阵操作。重点练习“二维数组作函数参数”和“矩阵转置”两类题。考试中先确定数组维度,再设计循环结构,最后检查边界和输出格式。