引子
接触指针和数组时,经常容易搞混两者结合后的意义,这里结合自己的体会总结下:
A) int *p1[10];
B) int (*p2)[10];
结论:
- A 为指针数组,全称为储存指针的数组。
p1本质是二级指针
。 - B 为数组指针,全称为指向数组的指针。也称行指针。
p2本质是一级指针
。
分析:
- 对于A项
- A中
p1
先和[10]
结合,成为数组,然后再和*
结合,成为int *
类型的数组 - 本质是数组,存储的数据是
int *
类型指针。 - 取固定间隔的指针地址后放入该指针数组,也可以模拟二维数组
- A中
- 对于B项
- B中
p2
首先和()
结合,也即是个指针 - 其次才和
[10]
结合,表示这个指针指向元素的个数也即列数有10个 - 别名,行指针
- B中
特别注意,如何理解A项中p1
本质是二级指针?分析如下:
p1[10]
是个数组,里面存的都是int *
类型的一级指针,p1
数组名本质是&p1[0]
,而p1[0]
是个一级指针,对一级指针取地址,那就是二级指针。- B中
p2
先和()
结合,成为指针(*p2)
,然后再和[]
结合,成为指向10个int大小类型
的指针,本质是指针,只是特地强调了该指针不是一个int变量,而是一个数组,含有10个int变量。作用相当于二维数组。 - B中指定了其指向的数组长度为10个int类型数据。通常我们喜欢直接用 int *p2,而省略后面的数组元素个数,以方便扩展。特殊地,当二维数组用行指针传递时,特别是第二个,10,则代表列数,不能省略。
备注:() * []
的三者结合优先级为 () > [] > *
。
举一反三
请试分析以下例子并分类数组指针和指针数组:
int *p1[10];
int (*p2)[10]; //相当于声明二维数组,[10]为二维列数
int a[10] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int b[10] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int c[11] = {
-1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int i;
for (i = 0; i < 10; i++) {
p1[i] = &a[i];
printf("%d ", *p1[i]); // 解引用p1[i]这个指针
}
printf("\n");
p2 = b;
printf("%d\n", p2[0][0]);
p2 = c;
printf("%d\n", p2[0][0]); // 如果是p2[1][0],则是在第0行10个元素后的下一行元素首地址,也就是跳过了10个元素
printf("%d\n", p2[0][10]); // 第一个0是整体行数的偏移,第二个才是列元素的偏移。
// 10表示第11个元素,虽然p2声明一列10个元素,但是本质是指针偏移操作,依然是可以解引用到c[11]的值。
输出结果为:
0 1 2 3 4 5 6 7 8 9
0
-1
10
补充说明
1. 函数参数中数组本质都是指针
- 不管是一维还是二维数组,数组在传递给函数参数时,C语言中统统都转化为了指针
- 一维数组被弱化成一级指针,二维数组经数入参后被弱化成了数组指针,
int p[4]
最终实质为int (*p)[4]
- 如,
a[i][j]
等价于* ( * (a + i) + j )
,其中*(a + i)
类似于a[i]
。 - 详细解释参考:C语言数组参数和指针参数的区别
2. 数组指针和指针数组本质都是一级指针
核心
:不管是数组指针还是指针数组,本质都是一级指针- 若是用动态分配内存的方式,来获得二维数组,需要用到二级指针
- 详细解释参考:C学习:动态申请二维数组的方法和二维数组和二级指针辨析