C语言小课堂:二维数组

C语言小课堂:二维数组
TANG JIAMEI- 什么是
二维数组
?
在C语言中,二维数组可以被理解为数组的数组,或者说是一个具有行和列结构的表格。它是一种用于存储同类型数据的集合,这些数据通过行索引和列索引来访问。
一维数组:像一条线,数据按顺序排列,通过一个下标(索引)访问,例如 arr[0]
, arr[1]
。
二维数组:像一个平面(表格),数据按行和列排列,通过两个下标(索引)访问,例如 arr[行索引][列索引]
。
声明语法:
数据类型 数组名[行数][列数];
数据类型
:数组中所有元素的数据类型(例如 int
, char
, double
等)。
数组名
:你给数组起的名字。
行数
:数组的行数,必须是正整数常量。
列数
:数组的列数,必须是正整数常量。
内存布局: 虽然我们把它想象成一个表格,但计算机内存是线性的。C语言中的二维数组在内存中是**按行优先(row-major)**存储的。这意味着第一行的所有元素存储在内存中是连续的,接着是第二行的所有元素,依此类推。
例如,一个 int arr[2][3];
的二维数组,其内存布局大致如下:
arr[0][0], arr[0][1], arr[0][2], arr[1][0], arr[1][1], arr[1][2]
- 何时用
二维数组
?
当你需要处理的数据具有表格结构或矩阵特性时,二维数组是你的不二之选。它能让你的代码更直观、更符合逻辑地表示这些数据。
典型应用场景:
矩阵运算:在线性代数中,矩阵是二维的,二维数组天生适合表示和操作矩阵(加法、乘法、转置等)。
图像处理:简单的灰度图像可以用一个二维数组表示,每个元素代表一个像素的灰度值。
游戏地图:棋盘、迷宫、扫雷等游戏的地图可以用二维数组来表示,每个元素代表地图上的一个格子(是墙、是路、是地雷等)。
学生成绩表:如果需要存储多个学生的多个科目的成绩,可以想象成一个表格:行代表学生,列代表科目。
座位安排:电影院、教室的座位布局。
为什么要用?因为二维数组能够模拟现实世界中常见的二维结构,使得数据的存储、访问和操作变得更加逻辑化和高效。如果没有二维数组,你需要用复杂的一维数组索引计算来模拟,这将大大增加代码的复杂度和出错率。
与关联知识点的联系与区别:
与一维数组:二维数组是更高维度的一维数组的扩展。一维数组是线性结构,二维数组是平面结构。理解了一维数组的索引和内存连续性,就能更好地理解二维数组。
与结构体数组:如果表格的每一行(或每一列)的数据类型不同,或者每一行代表一个“实体”(比如学生信息:姓名、学号、成绩),那么结构体数组可能是更好的选择。二维数组要求所有元素都是同类型。
与指针:二维数组名在很多情况下可以看作是指向其第一个元素(即第一行)的指针。深入理解指针有助于理解二维数组的底层机制和函数传参。
- 怎么用
二维数组
?
3.1 声明和初始化
声明:
int matrix[3][4]; // 声明一个3行4列的整数二维数组
char board[8][8]; // 声明一个8行8列的字符二维数组,可以用于棋盘
初始化: 你可以在声明时进行初始化。
完全初始化:按行顺序提供所有元素。
int matrix[2][3] = {
{1, 2, 3}, // 第0行
{4, 5, 6} // 第1行
};
等价于:
int matrix[2][3] = {1, 2, 3, 4, 5, 6}; // 内存按行优先排列
部分初始化:未初始化的元素会被自动设置为0(如果是全局或静态数组)或垃圾值(如果是局部自动数组)。
int matrix[2][3] = {
{1, 2}, // 第0行:matrix[0][2]为0
{4} // 第1行:matrix[1][1]和matrix[1][2]为0
};
省略行数:如果初始化时提供了所有元素,并且指定了列数,编译器可以自动推断行数。
int matrix[][3] = { // 编译器会根据元素数量和列数3推断出行数是2
{1, 2, 3},
{4, 5, 6}
};
注意:列数不能省略,因为编译器需要知道每行有多少元素才能正确计算内存地址。
3.2 访问元素
使用行索引和列索引来访问特定元素。索引从0开始。
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
// 访问第一行第一列的元素(值为1)
int element_0_0 = matrix[0][0];
// 访问第二行第三列的元素(值为6)
int element_1_2 = matrix[1][2];
// 修改元素
matrix[0][1] = 10; // 将第一行第二列的元素(原为2)修改为10
3.3 遍历数组
通常使用嵌套的 for
循环来遍历二维数组的所有元素。外层循环控制行,内层循环控制列。
1 |
|
输出:
遍历二维数组:
1 2 3 4
5 6 7 8
9 10 11 12
通过以上讲解,你应该对C语言二维数组有了全面的认识。它是学习更复杂数据结构和算法的基础,掌握它将为你的编程之路打下坚实的基础!
💻 配套习题
第一题 (选择题)
以下关于C语言二维数组的说法,哪项是错误的?
A. 二维数组可以看作是数组的数组。
B. 声明二维数组时,可以省略行数,但不能省略列数。
C. 二维数组在内存中是按列优先(column-major)存储的。
D. 二维数组的元素可以通过 arr[行索引][列索引]
的方式访问。
第二题 (填空题)
声明一个名为 grades
的二维数组,用于存储5个学生(行)的3门课程(列)的整数成绩,且所有学生的所有成绩都初始化为0。请写出其声明和初始化代码:int grades[][] = {______};
第三题 (代码输出题)
阅读下面的C代码,并写出其输出结果:
#include <stdio.h>
int main() {
int arr[2][2] = {{10, 20}, {30, 40}};
printf("%d %d\n", arr[0][1], arr[1][0]);
return 0;
}
第四题 (概念辨析题)
判断题:一个 int matrix[2][3];
的二维数组,其元素 matrix[1][0]
在内存中紧邻着 matrix[0][2]
。
A. 正确
B. 错误
第五题 (编程题)
编写一个C程序:
声明一个 3x3
的整型二维数组 magic_square
。
通过嵌套循环,将数组的每个元素初始化为 (行索引 + 1) * (列索引 + 1)
。
遍历并打印这个二维数组,每行元素打印在一行,并用空格分隔。
例如: 当 i=0, j=0
时,magic_square[0][0]
为 (0+1)(0+1) = 1
当 i=0, j=1
时,magic_square[0][1]
为 (0+1)(1+1) = 2
… 当 i=2, j=2
时,magic_square[2][2]
为 (2+1)*(2+1) = 9
预期输出格式:
1 2 3
2 4 6
3 6 9
答案: 见下方代码及讲解
1 |
|
💡 习题讲解
第一题 (选择题)
答案: C
讲解:
A. 二维数组可以看作是数组的数组。 这是正确的。在C语言中,int arr[3][4]
可以被理解为包含3个元素的一维数组,每个元素又是一个包含4个 int
类型元素的一维数组。
B. 声明二维数组时,可以省略行数,但不能省略列数。 这是正确的。例如 int matrix[][3] = {{1,2,3},{4,5,6}};
。编译器需要列数来确定每行的大小,从而计算内存地址。
C. 二维数组在内存中是按列优先(column-major)存储的。这是错误的! C语言中的二维数组是**按行优先(row-major)**存储的。这意味着同一行的元素在内存中是连续存放的。例如 arr[0][0], arr[0][1], arr[0][2], arr[1][0], ...
。Fortran等语言是按列优先存储的。
D. 二维数组的元素可以通过 arr[行索引][列索引]
的方式访问。 这是正确的,这是二维数组元素访问的标准语法。
第二题 (填空题)
答案: int grades[5][3] = {0};
讲解:
int grades[5][3]
:根据题目要求,5个学生对应行数,3门课程对应列数。所以声明为 [5][3]
。
= {0}
:这是C语言中初始化数组的一个技巧。当初始化列表只提供一个值(例如 0
)时,如果数组的元素数量多于初始化列表中提供的值,那么所有未显式初始化的元素都会被自动设置为0。对于局部变量,这是一种非常方便的将整个数组清零的方法。
第三题 (代码输出题)
答案: 20 30
讲解:
int arr[2][2] = {{10, 20}, {30, 40}};
:声明并初始化一个 2x2
的二维数组。
arr[0][0]
是 10
arr[0][1]
是 20
arr[1][0]
是 30
arr[1][1]
是 40
printf("%d %d\n", arr[0][1], arr[1][0]);
:
arr[0][1]
对应的值是 20
。
arr[1][0]
对应的值是 30
。
%d %d\n
会按顺序打印这两个整数,中间用空格分隔,末尾换行。 因此,输出结果是 20 30
。
第四题 (概念辨析题)
答案: A
讲解:A 选项正确 (即原命题正确)。 这是一个关于C语言二维数组内存布局的关键概念。C语言中的二维数组是**按行优先(row-major)**存储的。 对于 int matrix[2][3];
:
内存中先是 matrix[0][0]
, matrix[0][1]
, matrix[0][2]
(第一行的所有元素)。
紧接着是 matrix[1][0]
, matrix[1][1]
, matrix[1][2]
(第二行的所有元素)。 所以,matrix[0][2]
是第一行的最后一个元素,matrix[1][0]
是第二行的第一个元素。在内存中,它们是紧挨着存储的。
易混淆点:理解行优先存储是避免数组越界和进行指针操作时非常重要的基础。
第五题 (编程题)
答案: 见上方代码及讲解
讲解:
#include <stdio.h>
:引入标准输入输出库,以便使用 printf
函数。
int magic_square[3][3];
:声明一个 3x3
的整型二维数组。
int rows = 3; int cols = 3;
:定义行数和列数常量,这样可以使代码更具可读性和可维护性,如果需要修改数组大小,只需修改这两个变量即可。
初始化循环:
for (int i = 0; i < rows; i++) { // 外层循环控制行 i
for (int j = 0; j < cols; j++) { // 内层循环控制列 j
magic_square[i][j] = (i + 1) * (j + 1); // 根据题目要求进行初始化
}
}
这里 (i + 1)
和 (j + 1)
是因为索引 i
和 j
从0开始,而题目要求从1开始计算乘积。
打印循环:
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%d ", magic_square[i][j]); // 打印当前元素,后跟一个空格
}
printf("\n"); // 每打印完一行后换行,使得输出呈现矩阵形式
}
这个循环结构和初始化循环类似,确保所有元素都被正确访问和打印。printf("\n");
是关键,它确保了每行数据在输出时都独占一行。
这个题目综合考察了二维数组的声明、初始化、通过索引访问以及使用嵌套循环进行遍历打印的能力。