第42章:指针数组与数组指针——int *p[5] 和 int (*p)[5] 的区别
说实话,这道题几乎是每个C语言面试官的必问题。我当年第一次看到这两个声明时,也懵了好一阵子。明明都是p和5,怎么一个是指针数组,一个是数组指针?
嗯,咱们今天就把这个「老冤家」彻底讲清楚。你只要记住一句话:看优先级,看结合性。
一、先看声明,别被符号绕晕
先上代码,咱们直接对比着看:
int *p[5]; // 指针数组
int (*p)[5]; // 数组指针
这两个声明,就差一对括号。但含义天差地别。
为什么会这样?因为C语言里,[] 的优先级高于 *。所以:
int *p[5]:p 先和 [5] 结合,说明 p 是一个数组,数组有5个元素。每个元素是什么?是int *,也就是指向整型的指针。所以叫「指针数组」——数组里存的是指针。int (*p)[5]:括号让 p 先和 * 结合,说明 p 是一个指针。这个指针指向什么?指向一个长度为5的整型数组。所以叫「数组指针」——指针指向一个数组。
核心口诀:
- 先看 p 和谁结合
- 和 [ ] 结合 → 数组
- 和 * 结合 → 指针
二、用图说话:SVG结构对比
我画了一张图,帮你直观理解这两个东西在内存里长什么样:
三、代码实战:动手写一写
光看理论不够,咱们直接上代码。我写两个例子,你一看就明白:
示例1:指针数组
#include <stdio.h>
int main() {
int a = 10, b = 20, c = 30;
int *p[3]; // 指针数组:能存3个指针
p[0] = &a;
p[1] = &b;
p[2] = &c;
for (int i = 0; i < 3; i++) {
printf("p[%d] = %d\n", i, *p[i]);
}
return 0;
}
输出:
p[0] = 10
p[1] = 20
p[2] = 30
你看,p 是一个数组,里面每个元素都是指针。我们用下标访问每个指针,再解引用拿到值。
示例2:数组指针
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int (*p)[5]; // 数组指针:指向一个长度为5的整型数组
p = &arr; // 注意:取的是整个数组的地址
for (int i = 0; i < 5; i++) {
printf("(*p)[%d] = %d\n", i, (*p)[i]);
}
return 0;
}
输出:
(*p)[0] = 1
(*p)[1] = 2
(*p)[2] = 3
(*p)[3] = 4
(*p)[4] = 5
这里 p 是一个指针,它指向整个数组。要访问数组元素,得先解引用拿到数组,再用下标。
小技巧:
我个人习惯这样记——看到 int *p[5],心里默念「p是数组,元素是指针」;看到 int (*p)[5],默念「p是指针,指向数组」。
另外,用 sizeof 也能帮你验证:
sizeof(p)在指针数组里是数组总大小(5 * 8 = 40字节,64位系统)sizeof(p)在数组指针里是指针本身大小(8字节)
四、避坑指南:我踩过的雷
我曾经在一个嵌入式项目里,需要管理多个传感器的数据缓冲区。我本来想定义一个指针数组,每个指针指向一个缓冲区。结果手一抖,写成了数组指针——
// 我本来想这样:
int *buf[3]; // 3个指针,每个指向一个缓冲区
// 结果写成了这样:
int (*buf)[3]; // 1个指针,指向一个长度为3的数组
编译没报错,但运行时数据全乱套了。因为 buf[1] 在第一种写法里是第二个指针,在第二种写法里是数组的第二个元素。排查了整整一个下午,最后发现是声明写错了。
重要提醒:
- 声明时一定要看清楚括号的位置
- 用
typedef可以简化复杂声明,比如:typedef int (*ArrayPtr)[5]; - 在函数参数中,数组指针常用于传递二维数组:
void func(int (*arr)[5])
五、常见用法对比表
| 特性 | int *p[5](指针数组) | int (*p)[5](数组指针) |
|---|---|---|
| 本质 | 数组,元素是指针 | 指针,指向数组 |
| sizeof(p) | 5 * 指针大小(如40字节) | 指针大小(如8字节) |
| p+1 的步长 | 一个指针的大小 | 整个数组的大小(5 * int) |
| 典型用途 | 字符串数组、函数指针表 | 二维数组的行指针、多维数组遍历 |
| 访问元素 | *p[i] 或 **(p+i) | (*p)[i] 或 p[0][i] |
六、总结一下
说白了,这两个东西的区别就是「谁是谁的容器」:
- 指针数组:数组是容器,里面装的是指针
- 数组指针:指针是容器,指向一个数组
你想想看,是不是这个理?
嗯,这道题其实不难,就是符号优先级容易搞混。我的建议是:每次写这类声明时,先想清楚你要的是什么——是要一堆指针,还是要一个指向数组的指针。想清楚了再下笔,括号该加就加,别省。
我在实际项目中,指针数组用得更多一些,尤其是做命令表、状态机跳转表的时候。数组指针则常在处理二维数组时出现,比如图像处理中的像素矩阵操作。两种都有用武之地,关键是要用对地方。