BeginMan blog

C语言的指针与数组种种关系

此篇为《C语言点滴》关于指针的总结

一. 指针与数组关系

  指针 数组
指针 指针指针 指针数组
数组 数组指针 数组数组

表1 指针与数组的组合

假设以int类型测试:

  • int **pp 定义了一个指向指针的指针(指针指针)
  • int *pa[5]定义了一个指针数组
  • int (*ap)[5]定义了一个数组指针
  • int aa[2][3]定义了一个数组数组,也就是一个二维数组
  指针 数组
指针 指针指针:int **pp 指针数组:int *pa[5]
数组 数组指针:int (*ap)[5] 数组数组:int aa[2][3]

表2:四个概念的定义

真理:

  1. 指针真理:一个xx 型指针应该指向一个xx 型地址。
  2. 数组真理:xx 型数组变量就是一个xx 型地址。

真理推导:

  • 根据真理1可知:一个指针型指针指向一个指针型地址;一个数组型指针指向一个数组型地址
  • 根据真理2可知:指针型数组变量就是一个指针型地址;数组型数组变量(二维数组)就是一个数组型地址。

综上推导:

  • 一个 指针型指针 可以指向一个 指针型数组变量, 上表第一行 pp = pa
  • 一个 数组型指针 可以指向一个 数组型数组(二维数组), 上表第二行 ap = aa
  指针变量 数组变量
char 类型 char *p char a[5]
指针类型 int **pp int *pa[5]
数组类型 int (*ap)[5] int aa[2][3]

表3 指针变量可指向对应数组变量

二. 指针指针、指针数组、数组指针

2.1 指针型指针

关于指针型指针,如:

int i = 5;
printf("i=%d, &i=%p\n", i, &i);

int *p = &i;
printf("*p=%d, p=%p, &p=%p\n", *p, p, &p);

int **pp = &p;
printf("*pp=%d, pp=%p, &pp=%p\n", **pp, pp, &pp);

打印输出:

i=5,    &i=0x004f5a6c
*p=5,   p=0x004f5a6c,   &p=0x004f5a5a
*pp=5,  pp=0x004f5a5a,  &pp=0x004f5a4c

每个变量,无论它保存的是什么,它本身也都有地址:

  • 指针变量p 保存的是变量i 的地址0x004f5a6c,但是p 本身的地址是0x004f5a5a。
  • 指针变量pp 中保存的是指针变量p 的地址0x004f5a5a。为了保存指针型变量p 的地址,我们必须定义一个指针型指针,那就是int **pp

如下图示:

图1:示意图

2.2 指针数组

对于一个指针数组,数组中保存的都是指针,数组名就是指针型地址。为了保存这种指针型地址,根据指针真理,我们自然应该用指针型指针了。

int *p[3],这是因为[]的优先级比*号高,所以int *p[3]会被编译器解释为int *(p[3])。这就是指针数组

见下:

char *a[] = {"人生苦短", "😁", "Python当歌", NULL};   // 指针数组
char **p;                                           // 指针指针
for (p = a; *p != NULL; p++) {
   printf("%s\n", *p);
}

打印输出:

人生苦短
😁
Python当歌

2.3 数组指针

如何定义一个数组型的指针?不能写成int *p[3],这是因为[]的优先级比*号高,所以int *p[3]会被编译器解释为int *(p[3])。这其实就是上面介绍过的指针数组,而不是我们要定义的数组指针。为了解决这个问题,我们用括号改变它的优先级,写成int (*p)[3]。这时(*p)是一个指针,指针指向的类型为int[3],这是一个一维数组型变量,符合我们的定义。

图2:对比一维和二维的数组指针实例

有了int (*p)[3]=array[2][3]的写法,我们对二维数组也可以利用指针来进行访问了,如下面所示:

  • p 即 &array[0]
    • 代表二维数组的首地址,第0 行的地址
    • 代表一维int 数组型变量int[]的地址
  • p+i 即 &array[i]
    • 代表第i 行的地址
    • 一维int 数组型变量int[]的地址
  • *(p+i)array[i]
    • 代表第i 行第0 列的地址
    • 一维int 数组型int[]变量,等价于int 类型的地址
  • *(p+i)+j&(array[i][j])
    • 代表第i 行第j 列的地址
    • int 类型的地址
  • *(*(p+i)+j )array[i][j]
    • 代表第i 行第j 列的元素
    • int 类型的值”

还是看代码测试吧:

int array[2][3] = {
       {101, 102, 103},
       {201, 202, 203}
};

int (*p)[3] = array;
printf("p=%p, &array[0]=%p\n", p, &array[0]);
printf(p == &array[0] ? "true\n" : "false\n");

/*
* out:
* p=0x7fff5cfa2540, &array[0]=0x7fff5cfa2540
* true
*/


printf("p+1=%p, &array[1]=%p\n", p+1, &array[1]);
printf(p+1 == &array[1] ? "true\n" : "false\n");

/*
* out:
* p+1=0x7fff5cfa254c, &array[1]=0x7fff5cfa254c
* true
*/

printf("*(p+1)=%p, array[1]=%p\n", *(p+1), array[1]);
printf(*(p+1) == array[1] ? "true\n" : "false\n");

/*
* out:
* *(p+1)=0x7fff5cfa254c, array[1]=0x7fff5cfa254c
* true
*/
printf("*(p+1)+2=%p, &(array[1][2])=%p\n", *(p+1)+2, &(array[1][2]));
printf(*(p+1)+2 == &(array[1][2]) ? "true\n" : "false\n");

/*
* out:
* *(p+1)+2=0x7fff5cfa2554, &(array[1][2])=0x7fff5cfa2554
* true
*/

printf("*(*(p+1)+2)=%d, array[1][2]=%d\n", *(*(p+1)+2), array[1][2]);
printf(*(*(p+1)+2) == array[1][2] ? "true\n" : "false\n");

/*
* out:
* *(*(p+1)+2)=203, array[1][2]=203
* true
*/

关键部分就是优先级, 如 () > [] > *

下面两篇文章基本搞定:

如果还不清楚,就看《深入理解C指针》这本不到200页的书。

(完)~