知识点精讲
一、二维数组基本语法
二维数组本质是"数组的数组",用于存储表格型(行×列)数据,核心用于批量处理二维结构数据,数组名代表首行首元素地址,双下标从0开始。
定义格式:
// 标准格式:数据类型 数组名[行数][列数];
int arr[3][4]; // 定义3行4列整型数组,可存12个int型数据
char str[5][10]; // 定义5行10列字符数组(5个字符串,每个最多9个字符)
float score[2][3]; // 定义2行3列浮点型数组
核心规则:
| 组成部分 |
要求 |
示例 |
注意事项 |
| 数据类型 |
任意基本数据类型 |
int、char、float |
数组内所有元素类型必须一致 |
| 数组名 |
符合标识符规则 |
arr、stu_score、num_matrix |
不能与变量名重复,数组名是地址常量 |
| 行/列数 |
正整数(常量/宏定义) |
3[4]、N[M](#define N 2 #define M 5) |
C89不支持变量定义行列数;列数不可省略(除非初始化) |
| 数组下标 |
行:0~行数-1;列:0~列数-1 |
arr[2][3](3行4列数组) |
行/列越界均会导致程序崩溃 |
内存特点:
二维数组在内存中按行连续存储(先存第0行所有列,再存第1行所有列...),例如int arr[2][3]的存储顺序:arr[0][0] → arr[0][1] → arr[0][2] → arr[1][0] → arr[1][1] → arr[1][2]。数组名是首行首元素地址(不可修改)。
二、二维数组的初始化
二维数组初始化支持按行/分段/部分初始化,避免使用未初始化的数组(元素值为随机数),常见初始化方式:
1. 按行连续初始化
// 按行顺序赋值,行数×列数需与值的个数匹配
int arr[2][3] = {1, 2, 3, 4, 5, 6};
// arr[0][0]=1, arr[0][1]=2, arr[0][2]=3; arr[1][0]=4, arr[1][1]=5, arr[1][2]=6
2. 分段初始化(推荐,可读性高)
// 用大括号分段表示每行,符合二维逻辑结构
int arr[2][3] = {{1,2,3}, {4,5,6}};
// 效果与连续初始化一致,考试高频写法
3. 部分初始化
// 只给部分元素赋值,剩余元素默认初始化为0
int arr[2][3] = {{1,2}, {4}};
// arr[0][0]=1, arr[0][1]=2, arr[0][2]=0; arr[1][0]=4, arr[1][1]=0, arr[1][2]=0
char ch[2][4] = {{'a'}, {'b'}}; // 字符数组部分初始化,剩余为'\0'
4. 省略长度初始化
// 可省略行数(由初始化行数自动确定),列数不可省略
int arr[][] = {1,2,3,4,5,6}; // 错误:列数不可省略
int arr[][3] = {{1,2}, {4,5,6}};// 正确:行数自动为2,列数固定3
char str[][] = {"hello", "world"};// 错误:列数不可省略
char str[][6] = {"hello", "world"};// 正确:行数2,列数6(含'\0')
5. 字符型二维数组(二维字符串)
// 存储多个字符串,每行一个字符串,列数需≥最长字符串长度+1('\0')
char name[3][10] = {"ZhangSan", "LiSi", "WangWu"};
// name[0] = "ZhangSan"(自动加'\0'),name[1] = "LiSi",name[2] = "WangWu"
三、二维数组的遍历
二维数组遍历需用双重循环(外层行循环,内层列循环),核心是控制行、列下标范围:
1. 标准双重for循环遍历(最常用)
#include
int main() {
int arr[2][3] = {{1,2,3}, {4,5,6}};
// 外层循环:遍历行(0~1)
for (int i = 0; i < 2; i++) {
// 内层循环:遍历列(0~2)
for (int j = 0; j < 3; j++) {
printf("arr[%d][%d] = %d\t", i, j, arr[i][j]);
}
printf("\n"); // 每行结束换行,模拟表格格式
}
return 0;
}
2. while循环双重遍历
#include
int main() {
int arr[3][2] = {{5,10}, {15,20}, {25,30}};
int i = 0, j = 0;
// 外层行循环
while (i < 3) {
j = 0; // 每行重置列下标
// 内层列循环
while (j < 2) {
printf("%d ", arr[i][j]);
j++;
}
printf("\n");
i++;
}
return 0;
}
四、二维数组常见操作(高频考点)
1. 二维数组求和(所有元素)
#include
int main() {
int arr[3][4] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};
int sum = 0;
// 双重循环遍历所有元素
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
sum += arr[i][j];
}
}
printf("二维数组总和:%d\n", sum); // 输出78
return 0;
}
2. 找二维数组最大值(含行/列下标)
#include
int main() {
int arr[2][3] = {{8,3,9}, {1,7,5}};
int max = arr[0][0]; // 假设第一个元素为最大值
int max_i = 0, max_j = 0; // 记录最大值的行、列下标
// 遍历所有元素
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
if (arr[i][j] > max) {
max = arr[i][j]; // 更新最大值
max_i = i; // 更新行下标
max_j = j; // 更新列下标
}
}
}
printf("最大值:%d,位置:arr[%d][%d]\n", max, max_i, max_j); // 输出9,arr[0][2]
return 0;
}
3. 二维数组矩阵转置(行列互换)
#include
int main() {
// 原数组:2行3列
int arr[2][3] = {{1,2,3}, {4,5,6}};
// 转置数组:3行2列
int trans[3][2];
// 实现转置:arr[i][j] → trans[j][i]
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
trans[j][i] = arr[i][j];
}
}
// 输出转置结果
printf("原数组:\n");
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", arr[i][j]);
}
printf("\n");
}
printf("转置数组:\n");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 2; j++) {
printf("%d ", trans[i][j]);
}
printf("\n");
}
return 0;
}
4. 二维数组按行冒泡排序(每行升序)
#include
int main() {
int arr[2][4] = {{5,2,9,1}, {8,3,7,4}};
int n = 4; // 每行元素个数
// 外层:遍历每一行
for (int row = 0; row < 2; row++) {
// 内层:对当前行进行冒泡排序
for (int i = 0; i < n-1; i++) {
for (int j = 0; j < n-1-i; j++) {
if (arr[row][j] > arr[row][j+1]) {
// 交换当前行的相邻元素
int temp = arr[row][j];
arr[row][j] = arr[row][j+1];
arr[row][j+1] = temp;
}
}
}
}
// 输出排序结果
printf("按行升序排序后:\n");
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
五、二维数组常见错误(高频考点)
- 列数省略:定义时省略列数(如int arr[2][]),仅初始化时可省略行数
- 下标越界:行/列下标超出范围(如int arr[2][3],访问arr[2][0]或arr[1][3])
- 初始化错误:赋值个数超过总行×列数(如int arr[2][3]={1,2,3,4,5,6,7})
- 遍历错误:双重循环内外层颠倒(列循环在外,行循环在内)
- 字符串二维数组:列数不足(如char str[2][5]={"hello","world"},无'\0'空间)
- 函数传参错误:二维数组传参时省略列数(如void func(int arr[][]))
// 错误示例1:列数省略
#include
int main() {
int arr[2][]; // 编译错误:列数不可省略
return 0;
}
// 错误示例2:下标越界
#include
int main() {
int arr[2][3] = {{1,2,3}, {4,5,6}};
printf("%d\n", arr[1][3]); // 列下标越界(最大2),结果随机
return 0;
}
// 错误示例3:函数传参省略列数
#include
void printArr(int arr[][]) { // 编译错误:列数必须指定
for(int i=0; i<2; i++) {
for(int j=0; j<3; j++) {
printf("%d ", arr[i][j]);
}
}
}
int main() {
int arr[2][3] = {{1,2,3}, {4,5,6}};
printArr(arr);
return 0;
}
例题精选
例题1:二维数组基本遍历(表格化输出)
定义3行2列整型数组,初始化值为{{10,20}, {30,40}, {50,60}},使用双重for循环遍历并按行输出(每行元素用制表符分隔)。
#include
int main() {
int arr[3][2] = {{10,20}, {30,40}, {50,60}};
// 外层行循环
for (int i = 0; i < 3; i++) {
// 内层列循环
for (int j = 0; j < 2; j++) {
printf("arr[%d][%d] = %d\t", i, j, arr[i][j]);
}
printf("\n"); // 每行结束换行
}
return 0;
}
运行结果:
arr[0][0] = 10 arr[0][1] = 20
arr[1][0] = 30 arr[1][1] = 40
arr[2][0] = 50 arr[2][1] = 60
解析:
1. 二维数组遍历必须用双重循环,外层控制行、内层控制列是标准逻辑;
2. 每行结束换行(printf("\n"))是模拟表格格式的关键,考试高频要求;
3. 若内外层循环颠倒(列在外、行在内),输出顺序会混乱(10 30 50 20 40 60)。
例题2:二维数组按列求和
定义2行3列浮点型数组{85.5,92.0,78.5,90.0,88.0,75.0},计算每列元素的和并输出。
#include
int main() {
float score[2][3] = {{85.5, 92.0, 78.5}, {90.0, 88.0, 75.0}};
float col_sum[3] = {0.0}; // 存储每列的和
// 外层列循环(按列求和)
for (int j = 0; j < 3; j++) {
// 内层行循环
for (int i = 0; i < 2; i++) {
col_sum[j] += score[i][j];
}
}
// 输出每列和
for (int j = 0; j < 3; j++) {
printf("第%d列和:%.1f\n", j+1, col_sum[j]);
}
return 0;
}
运行结果:
第1列和:175.5
第2列和:180.0
第3列和:153.5
解析:
1. 按列求和需将列循环作为外层、行循环作为内层,与按行求和逻辑相反;
2. 用数组col_sum存储每列和,避免重复计算,是优化写法;
3. 浮点型求和需用float/double类型,避免整数除法精度丢失。
例题3:二维数组找每行最大值
定义3行4列数组{{5,8,2,9}, {3,7,1,6}, {4,0,8,2}},找出每行的最大值并输出其值和列下标。
#include
int main() {
int arr[3][4] = {{5,8,2,9}, {3,7,1,6}, {4,0,8,2}};
// 遍历每一行
for (int i = 0; i < 3; i++) {
int row_max = arr[i][0]; // 假设每行第一个元素为最大值
int col_idx = 0; // 记录每行最大值的列下标
// 遍历当前行的每一列
for (int j = 1; j < 4; j++) {
if (arr[i][j] > row_max) {
row_max = arr[i][j];
col_idx = j;
}
}
printf("第%d行最大值:%d,列下标:%d\n", i+1, row_max, col_idx);
}
return 0;
}
运行结果:
第1行最大值:9,列下标:3
第2行最大值:7,列下标:1
第3行最大值:8,列下标:2
解析:
1. 找每行最大值需外层行循环,内层列循环比较当前行元素;
2. 每行初始化最大值为该行第一个元素,避免遗漏;
3. 同时记录列下标是考试高频考点,需注意下标从0开始。
例题4:二维数组矩阵转置(原地转置,方阵)
定义3×3方阵{{1,2,3}, {4,5,6}, {7,8,9}},实现原地转置(不使用额外数组),输出转置前后结果。
#include
int main() {
int arr[3][3] = {{1,2,3}, {4,5,6}, {7,8,9}};
// 输出原数组
printf("原数组:\n");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", arr[i][j]);
}
printf("\n");
}
// 原地转置(仅交换上三角与下三角元素,避免重复交换)
for (int i = 0; i < 3; i++) {
for (int j = i+1; j < 3; j++) {
int temp = arr[i][j];
arr[i][j] = arr[j][i];
arr[j][i] = temp;
}
}
// 输出转置结果
printf("转置数组:\n");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
运行结果:
原数组:
1 2 3
4 5 6
7 8 9
转置数组:
1 4 7
2 5 8
3 6 9
解析:
1. 原地转置仅适用于方阵(行=列),核心是j从i+1开始,避免对角线元素重复交换;
2. 非方阵无法原地转置,需定义新数组存储转置结果;
3. 转置的核心逻辑是arr[i][j]与arr[j][i]交换,是考试核心考点。