C 二维数组
更新时间:2026-08-26。本文是
languages/c/主题入门层第 26 篇。二维数组 = "数组的数组"。它在内存里其实还是一长条——理解这点,遍历性能、传参、以及后面指针数组的坑就都清楚了。
本文要回答的问题
- 二维数组在内存里是"格子"还是"一长条"?
- 怎么声明、初始化和遍历一个矩阵?
- 为什么遍历顺序会影响性能?
一、声明与内存排布
c
int matrix[3][4]; // 3 行 4 列逻辑上是 3×4 的格子,物理上是一段 12 个 int 连续排列的内存:
text
行0: [m[0][0]] [m[0][1]] [m[0][2]] [m[0][3]]
行1: [m[1][0]] [m[1][1]] [m[1][2]] [m[1][3]]
行2: [m[2][0]] [m[2][1]] [m[2][2]] [m[2][3]]
内存连续:m[0][0], m[0][1], ..., m[0][3], m[1][0], ...C 是行优先存储:一行排完再排下一行。matrix[1] 本身就是"指向第 1 行"的数组。
二、初始化
c
int a[2][3] = {{1, 2, 3}, {4, 5, 6}}; // 分行初始化(推荐)
int b[2][3] = {1, 2, 3, 4, 5, 6}; // 连续初始化:效果一样
int c[2][3] = {{1}, {4}}; // 部分初始化:补 0
int d[][3] = {{1,2,3},{4,5,6}}; // 行数可省,列数不可省!关键规则:只有"第一个维度"可以省略(编译器要靠列数算偏移)。int d[][3] 合法,int d[2][] 是编译错误。
三、遍历:双重循环
c
#include <stdio.h>
int main(void) {
int matrix[3][4];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
matrix[i][j] = i * 4 + j; // 给每个格子赋值
}
}
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%2d ", matrix[i][j]);
}
printf("\n"); // 每行结尾换行,打印成矩阵形状
}
return 0;
}遍历顺序影响性能(本站主线!):按"行优先"(外层 i、内层 j)访问,内存是顺序访问,缓存命中率高;交换成"列优先"(外层 j、内层 i)访问同一列,每次跳一整行,缓存命中率暴跌。大数据量下两种写法能差出数倍——L3 缓存子系统有实测。
四、传参:列数是签名的一部分
c
void print_matrix(int m[3][4], int rows) { ... } // 完整维度
void print_matrix2(int m[][4], int rows) { ... } // 行数可省,列数必须写
// int m[][] 是错的:编译器算不出地址偏移二维数组传参时,列数(第二个维度)必须写——因为 m[i][j] 的地址 = 起始 + (i × 列数 + j) × 元素大小,编译器不知道列数就没法寻址。
五、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
int d[2][] | 编译错误 | 列数必须写 |
| 省略初始化但想全零 | 垃圾值 | = {0} |
| 列优先遍历 | 性能差数倍 | 按行优先遍历 |
| 传参省略列数 | 编译错误/越界 | 签名带上列数 |
| 下标越界(行列搞混) | UB | 记住 [行][列] |
六、与本站主线衔接
一句话总结
二维数组 = 数组的数组,内存里还是一长条(行优先连续排布);只有第一个维度可省略、传参时必须写列数、遍历按行优先——顺序访问缓存友好,这个"顺序"的直觉以后会在性能分析里反复用到。