10、数组(二):二维数组的定义与初始化、二维数组的遍历、多维数组的概念
好,咱们接着聊数组。上一章我们把一维数组掰开揉碎讲清楚了,这一章来看二维数组。说实话,二维数组在实际开发中比一维数组更常见——你想想看,一张图片、一个棋盘、一张Excel表格,本质上都是二维数组。我当年刚入行时,第一个正经项目就是做图像处理,那时候才真正体会到二维数组有多重要。
二维数组的定义与初始化
二维数组,说白了就是「数组的数组」。你可以把它想象成一个表格,有行有列。定义方式也很直白:
// 定义一个3行4列的二维数组
int matrix[3][4];
这里 [3] 是行数,[4] 是列数。我个人习惯把行数写在前面,列数写在后面,这样更符合「先行后列」的直觉。
初始化方式有好几种,我挑最常用的给你看:
// 方式1:按行初始化(最推荐)
int arr1[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
// 方式2:线性初始化(按内存顺序)
int arr2[2][3] = {1, 2, 3, 4, 5, 6};
// 方式3:部分初始化(未指定的元素自动补0)
int arr3[2][3] = {
{1, 2},
{4}
};
// 结果:{{1, 2, 0}, {4, 0, 0}}
这里有个容易踩的坑:定义时可以省略行数,但不能省略列数。为什么?因为编译器需要知道每行有几个元素,才能算出每行在内存里占多少字节。
// 正确:省略行数
int arr[][3] = {{1,2,3}, {4,5,6}};
// 错误:省略列数
// int arr[2][] = {{1,2,3}, {4,5,6}}; // 编译报错
二维数组的遍历
遍历二维数组,标准做法是嵌套循环。外层循环走行,内层循环走列。我在项目中见过有人把行列搞反了,结果数据全乱套了。
#include <stdio.h>
int main() {
int matrix[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++) {
printf("%3d ", matrix[i][j]);
}
printf("\n"); // 每行结束后换行
}
return 0;
}
输出效果:
1 2 3 4
5 6 7 8
9 10 11 12
遍历顺序其实有两种:行优先和列优先。C语言是行优先的——也就是说,matrix[0][0] 后面紧跟着 matrix[0][1],而不是 matrix[1][0]。这个特性在性能优化时很有用:如果你按列遍历,CPU缓存命中率会大幅下降。
// 行优先遍历(快)
for (int i = 0; i < ROWS; i++)
for (int j = 0; j < COLS; j++)
process(matrix[i][j]);
// 列优先遍历(慢)
for (int j = 0; j < COLS; j++)
for (int i = 0; i < ROWS; i++)
process(matrix[i][j]);
嗯,这里要注意:数据量小的时候感觉不出来,但一旦矩阵是 1000x1000 以上,两种遍历方式的速度差距能到好几倍。
多维数组的概念
二维数组再往上,就是三维、四维……统称多维数组。定义方式就是继续加方括号:
// 三维数组:2个平面,每个平面3行4列
int cube[2][3][4];
你可以把它想象成一个魔方,或者一摞表格。实际开发中,三维数组已经很少见了,四维以上基本只在科学计算或机器学习里出现。我做了这么多年C语言开发,用到三维数组的场景一只手数得过来。
多维数组的初始化也是逐层嵌套:
int cube[2][2][3] = {
{
{1, 2, 3},
{4, 5, 6}
},
{
{7, 8, 9},
{10, 11, 12}
}
};
遍历三维数组需要三层循环:
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
for (int k = 0; k < 3; k++) {
printf("cube[%d][%d][%d] = %d\n", i, j, k, cube[i][j][k]);
}
}
}
- 二维数组是「数组的数组」,内存中按行优先存储
- 定义时行数可省略,列数不可省略
- 遍历时注意行列顺序,避免越界
- 多维数组本质相同,只是维度增加
最后给你看一张图,把二维数组的内存布局和遍历逻辑串起来:
这张图左边是逻辑上的表格,右边是内存中实际的线性存储。你看,每一行的数据在内存里是连续挨着的,这就是行优先。遍历时按行走,CPU缓存能一次性加载一整行,效率最高。
好了,二维数组的核心内容就这些。记住:定义时列数不能省,遍历时先行后列,内存布局是行优先。把这三点刻在脑子里,二维数组这块就稳了。