第五章 数组 - 第二节 二维数组学习资料
概述
二维数组是C语言中数组的扩展形式,是处理矩阵数据、表格信息和多维数据的基础结构。本节详细介绍二维数组的定义、初始化、访问方法及其在实际编程中的应用。通过本节学习,考生将能够理解二维数组的内存布局,掌握二维数组的操作技巧,熟练运用二维数组解决实际问题,为计算机等级考试二级C语言程序设计部分的考查内容打下坚实基础。
学习目标:
- 理解二维数组的概念及声明方法
- 掌握二维数组的初始化及元素访问
- 熟悉二维数组在实际问题中的应用
- 避免使用二维数组时的常见错误
核心概念
1. 数组
数组是由相同数据类型元素组成的有序集合,内存中连续排列。二维数组是数组的数组,具有行和列的结构。
2. 二维数组
二维数组可以看作是一个矩阵,具有固定的行数和列数。它是通过两个下标访问的数组元素,格式通常为array[row][column]。
3. 内存布局
二维数组在内存中是以行优先(Row-major order)存储的,即先存储第一行的所有元素,再存储第二行,依此类推。
4. 下标访问
二维数组元素通过两个下标访问,分别代表行号和列号,下标从0开始。
5. 初始化
二维数组可以整体初始化,也可以部分初始化,未初始化的元素会被自动赋值为0。
原理分析
二维数组的声明与内存分配
二维数组的声明格式为:
数据类型 数组名[行数][列数];
例如:
int matrix[3][4];
这表示声明了一个3行4列的二维整型数组,计算机为其分配连续的内存空间,总共3 * 4 = 12个int大小的空间。
内存中元素的排列顺序是:
| 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] |
这种按行顺序存储的方式决定了访问时的效率和指针运算。
指针与二维数组
二维数组名代表数组首元素的地址,但二维数组名本身是指向一维数组(即一行)的指针。具体来说,matrix是int (*)[4]类型的指针,指向含4个int元素的一维数组。
访问元素的地址可以通过指针计算实现:
&matrix[i][j] == (matrix + i) + j
这体现了二维数组是数组的数组结构。
详细内容
1. 二维数组的声明与定义
二维数组的声明必须指定行数和列数,格式规范如下:
type arrayName[rows][cols];
- 行数表示数组中有多少个一维数组
- 列数表示每个一维数组中元素的个数
示例:
int scores[5][3];
声明了一个5行3列的二维整型数组,通常表示5个学生3门课程的成绩。
注意:
- 行数和列数均应为正整数常量表达式
- 维数必须明确,不能省略
2. 二维数组的初始化
二维数组的初始化可以有多种方式:
(1) 完整初始化
int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
(2) 部分初始化
int matrix[2][3] = {1, 2, 3};
此时,第一行元素被初始化为1,2,3,第二行元素全部为0。
(3) 简写初始化
int matrix[2][3] = { {1, 2}, {3} };
未初始化的元素自动赋0。
初始化时,缺少的元素会自动补0,确保数组元素完整。
3. 元素访问
二维数组元素访问格式:
arrayName[row][column]
例如:
int val = matrix[1][2];
表示访问第2行第3列元素。
注意:
- 行列索引均从0开始
- 访问时必须保证索引不越界,否则会导致未定义行为
4. 二维数组的遍历
通常使用嵌套循环遍历二维数组:
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
5. 二维数组与函数传参
二维数组作为函数参数时,必须明确列数:
void printMatrix(int matrix[][3], int rows);
行数可以省略,列数必须指定,因为编译器需要知道每行的元素数量以进行指针计算。
指针形式也可:
void printMatrix(int (*matrix)[3], int rows);
6. 动态二维数组
二维数组大小固定,若需动态大小,可以使用指针数组或动态分配内存,此部分超出本节重点,考生可在拓展内容中进一步学习。
实例分析
实例一:成绩表的二维数组管理
背景:有4个学生,每个学生有3门课程成绩,存储并计算每个学生的平均成绩。
代码示例:
#include <stdio.h>
int main() {
int scores[4][3] = {
{85, 90, 78},
{88, 76, 92},
{90, 91, 89},
{70, 80, 85}
};
for (int i = 0; i < 4; i++) {
int sum = 0;
for (int j = 0; j < 3; j++) {
sum += scores[i][j];
}
printf("Student %d average: %.2f\n", i+1, sum / 3.0);
}
return 0;
}
分析:
- 使用二维数组存储数据,方便管理
- 嵌套循环遍历实现计算
结论:二维数组结构清晰,适合存储矩阵类数据。
实例二:矩阵转置操作
背景:实现3x3矩阵的转置功能。
代码示例:
#include <stdio.h>
void transpose(int src[3][3], int dest[3][3]) {
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 3; j++) {
dest[j][i] = src[i][j];
}
}
}
int main() {
int matrix[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
int trans[3][3];
transpose(matrix, trans);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", trans[i][j]);
}
printf("\n");
}
return 0;
}
分析:
- 通过交换行列索引,实现矩阵转置
- 体现了二维数组访问的灵活性
结论:二维数组适合矩阵相关的数学运算。
实例三:二维数组作为函数参数
背景:编写函数打印任意4列的二维数组。
代码示例:
#include <stdio.h>
void printArray(int arr[][4], int rows) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
int main() {
int data[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
printArray(data, 3);
return 0;
}
分析:
- 函数参数指定列数,行数灵活
- 保证内存访问正确
结论:二维数组作为参数传递时必须指定列数。
常见误区
- 误区:忽略二维数组列数声明
错误示例:
void func(int arr[][], int rows); // 错误
正确做法:
void func(int arr[][cols], int rows);
列数必须明确。
- 误区:二维数组越界访问
访问索引超出数组范围会导致未定义行为,可能出现程序崩溃或错误结果。
正确做法:在访问前确保索引合法。
- 误区:二维数组初始化不完整导致垃圾值
未完全初始化的元素可能含有随机值。
正确做法:初始化时确保所有元素赋值,或利用大括号补全。
- 误区:二维数组名理解错误
二维数组名不是简单的指针,而是指向含固定列数的一维数组的指针。
- 误区:动态二维数组处理不当
直接使用普通二维数组无法动态扩展大小,需使用指针数组或动态内存分配。
应用场景
- 矩阵计算:如图像处理、数学计算中的矩阵乘法、转置等
- 游戏地图存储:二维数组可存储地图格子信息,如棋盘、迷宫
- 表格数据管理:学生成绩、财务报表等结构化数据存储
- 图形显示:二维数组存储像素信息,便于图形处理
- 多维数据处理:如气象数据、科学实验数据的存储和分析
知识拓展
- 多维数组:二维数组是多维数组的特例,三维及以上数组拥有更多维度,适合更复杂数据结构。
- 指针与数组:深入理解数组与指针的关系,掌握如何用指针操作二维数组。
- 动态二维数组:使用
malloc进行二维数组动态内存分配,解决数组大小固定限制。 - 数组与结构体结合:结合结构体管理复杂数据,如二维结构体数组。
总结回顾
本节围绕二维数组展开,系统讲解了二维数组的定义、声明、初始化、访问及其内存布局原理。通过典型代码实例,掌握了二维数组的遍历、函数传参以及矩阵操作技巧。明确列出了常见误区,帮助考生避免典型错误。最后结合实际应用场景,理解二维数组的重要性。对二维数组的深入理解和灵活应用是二级C语言程序设计考试的重要内容之一。
掌握本节内容,将为解决复杂数据结构问题提供坚实基础。