C学习:数组指针与指针数组辨析

引子


接触指针和数组时,经常容易搞混两者结合后的意义,这里结合自己的体会总结下:

A) int *p1[10];
B) int (*p2)[10];

结论:

  • A 为指针数组,全称为储存指针的数组。 p1本质是二级指针
  • B 为数组指针,全称为指向数组的指针。也称行指针。p2本质是一级指针

分析:

  • 对于A项
    • A中 p1先和[10]结合,成为数组,然后再和 * 结合,成为 int * 类型的数组
    • 本质是数组,存储的数据是int *类型指针。
    • 取固定间隔的指针地址后放入该指针数组,也可以模拟二维数组
  • 对于B项
    • B中p2首先和()结合,也即是个指针
    • 其次才和[10]结合,表示这个指针指向元素的个数也即列数有10个
    • 别名,行指针

特别注意,如何理解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. 数组指针和指针数组本质都是一级指针

参考链接


  1. C语言指针数组和数组指针

  2. 二维数组名的本质

  3. 一维数组、二维数组 和 一维指针、二维指针的本质区别

  4. C语言指针数组和数组指针–精讲