C 语言指针与多维数组
更新时间:2026-08-26。本文是
languages/c/主题高手层文档。
本文要回答的问题
- 二维数组在内存里是"一行接一行"吗?
int *p[3]和int (*p)[3]有什么不同?- 怎么动态分配一个二维数组?三种写法差在哪?
一、连续 vs 分段
| 形式 | 内存布局 | 适用 |
|---|---|---|
int a[3][4] | 连续 12 个 int,行内元素相邻 | 固定维度、局部性好 |
int *p[3](指针数组) | 3 个指针,各自指向一段 | 每行长度不同 |
int **pp(动态) | 指针的指针,逐行 malloc | 完全动态 |
int a[3][4] 是一维连续存储的"数组的数组"——a[1] 紧跟在 a[0] 后,遍历时缓存友好;指针数组和动态二维则是分段布局,每行可能不在同一页。
把两种布局摆在一起最直观:左边 int a[3][4] 是 12 个 int 排成一条线,右边 int *p[3] 是 3 个指针各自指向散落的行:

左边一整块连续(缓存友好、一次 malloc),右边每行单独分配(行可不等长,但多三次 malloc、地址分散)。这就是"二维"语法糖背后两种完全不同的内存真相。
二、声明辨析
c
int *p[3]; /* 指针数组:3 个 int* */
int (*p)[3]; /* 数组指针:指向"含 3 个 int"的数组 */
int **pp; /* 二级指针:常用于动态二维 */理解优先级:[] 比 * 结合更紧,所以 int *p[3] 先看 p[3] 是数组、元素是 int*。
三、动态分配三种写法
- 连续一次分配:
malloc(rows * cols * sizeof(int)),用a[r*cols+c]访问——最快、最省。 - 指针数组逐行分配:
malloc每行,a[r][c]自然语法——行变长方便,但多 N 次 malloc、局部性差。 - 二级指针:
int **a = malloc(rows * sizeof(int*))再逐行——与 2 类似。
函数传参:固定列数用 int a[][4];动态用 int **a 或 int *a + 手动下标。
实测:二维数组的连续性
c
int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int *flat = (int *)a; // 把二维当一维看
printf("a[1][0]=%d, flat[4]=%d\n", a[1][0], flat[4]);实测:
text
二维数组连续内存: a[1][0]=5, flat[4]=5 ← 相等,证明连续
数组指针: (*arr_ptr)[2]=30a[1][0](第二行第一个元素)和 flat[4](第 5 个元素)是同一块内存——因为 int a[3][4] 在内存里就是连续的 12 个 int,a[1][0] 的偏移正好是 4 个 int。
四、与入门层的衔接
一句话总结
"二维"只是语法糖:固定二维是连续内存、指针数组是分段内存,动态分配选"一次大 malloc + 手动下标"既快又省,函数传参看清声明优先级。
本文已完成填充:示例代码与实测数据已补齐。