一、指针数组和数组指针在定义上的不同
int* arr[3]; //定义了一个指针数组
int (*arr) [3]; //定义了一个数组指针
1、本质区别
指针数组和数组指针在本质上就有区别,指针数组是一个数组,数组里存放指针,数组指针是一个指针,指向一个数组。
2、原理
这两个定义区别就在于数组指针的定义给*arr
带了一对括号,使其优先匹配,所以问题就在于运算符优先级,[]
优先级高于*
,所以在指针数组的定义中,arr
优先和[]
匹配,指明定义了一个数组,而且是int*
类型的数组。而数组指针的定义中,我们先让*
和arr
匹配,指明这是一个指针,并且是指向int [3]
类型的指针。
3、语法
指针数组在定义时,因为是定义数组,所以[]
中可以不给值,由初始化列表指定其元素个数。而数组指针在定义的时候[]
中必须给值,因为这是定义指针,指针指向的数组有多少个元素必须确定,换句话说,如果[]
中不给值,int []
是一个不完整的类型,也就是指针指向了一个不完整的类型,这是不合理的。
数组指针是一个指针变量,它接收的是一个和它指定类型相同的数组的地址。
二、再谈数组指针
这里主要看一下下边两行代码的区别。
//arr是int[3]类型的数组
int * p = arr;
int (*q) [3] = &arr;
1、先明确他们本质上的不同
p
是一个int *
类型的指针,指向的应该是int
型的变量,q
是一个int (*) [3]
类型的指针,指向的应该是int [3]
类型的数组。
2、共同点
数组名在大多场合下指的是数组首元素的地址,所以将arr
赋给p
意思是p
指向arr[0]
这个int型的空间,将&arr
赋给q意思是q
指向arr
(这里的arr指的是整个数组)。
(1)地址空间
printf("%p\n", p);
printf("%p\n", q);
通过这个可以看出,对p
和q
指针按地址打印时,发现他们是一样的,从某种意义上来说,p
和q
指向同一空间,或者说指向的空间首地址相同。
(2)访问数组
printf("%d\n", p[1]);
printf("%d\n", (*q)[1]);
p
和q
都可以通过其特定的方式以相同的下标去访问到相同的数组空间。虽说代码看起来有些许不同,但是其对数组的访问所需下标是对应的。
3、结合共同点看本质变不同
虽说两者有一定的共同点,但是其本质还是不同的,而且看到(*q)[1]
这样的代码之后会很好奇q[1]
是一个怎样的状况呢,我们结合两个相同点来看一看。
铺垫:在指针和数组中的下标访问符结合起来的时候,会起到和数组相同的效果。比如上述中的p[1]
可以访问到arr[1]
中的元素,其原理性相当于是指针和整数相加,p[1] == *(p + 1)
, 而p + 1
的意思是在p
的基础上往后偏移一个p
所指向类型大小的空间。这就使得指针可以像数组那样用下标访问符来访问内存空间。
有了这个知识点,我们来看这两行代码
printf("p:%p q+1:%p\n", p, p + 1);
printf("q:%p q+1:%p\n", q, q + 1);
根据代码和结果分析一下,可以看到,p
和q
地址相同,但是p+1
和q+1
地址不同,算一下其差值就会发现,p+1
和p
相差4个字节,q+1
和q
相差12个字节,而我们的数组是int [3]
类型,一个元素是四个字节,整个数组是十二个字节,这就完全解释的通了,虽然p
和q
看似其地址空间相同,但是他们一个是指向数组中的一个元素,而另外一个指向整个数组。
还有上边提到了q[1]
,现在可以理解了,q[1] == *(q + 1)
,其打印的地址空间和q + 1
相同,原因是q + 1
为q往后偏移一个数组空间的首地址,q+1
还是一个指向和原数组相同类型的指针,而*(q + 1)
得到的是相当于和原数组相同的一个数组(虽然它不存在),那么打印这个数组空间就是打印其首元素地址,和q + 1
一样。
三、总结
指针数组和数组指针有着本质上的不同,对数组指针进行深度剖析之后,会发现它在内存空间上和原理性上都于普通指针不同,以至于再去想一想都不知道该说他是一个一级指针还是二级指针,总之在想清楚这个之前必须对数组和指针分别理解清楚,理解透了之后,将两者放在一起从某种意义上说可以说数组就是指针。