1、数组指针
A、什么是数组指针
数组指针,顾名思义,是一个指针变量,指向的对象是一个数组
这里有几点需要做出解释:
1、为什么是&arr 。arr是数组名,其本质上是数组首元素的地址,但数组指针要求指向的对象是一个数组,因而我们需要的是一个数组的地址,而&arr得到的就是arr这整个数组的地址。
2、为什么*p要加括号。这涉及到优先级的问题:[ ]这个操作符的优先级高于*。如果不加括号(),变量P会先与[ ]结合,此时p就成为一个数组,一个指向整型指针的数组
3、int (*p)[5]中的5可以省略吗。这个5不可以省略,如果省略,编译器会默认[ ]中的数为0,这样类型就不匹配了:省略5,默认p指向的类型为int[0](实际上,这个也是有问题的,因为数组元素个数必须大于0),而实际上的类型应该是int[5],二者类型不匹配
B、数组指针如何使用
数组指针通常与二维数组结合使用。
a、二维数组的数组名
在讨论数组指针的使用之前,我们需要研究一下二维数组。数组名是数组首元素的地址,对于二维数组而言,它的首元素是什么呢?
二维数组可以看成是由若干一维数组组成的,其中二维数组中的一行元素便相当于一个一维数组。因此,二维数组的首元素实际上就是二维数组第0行元素所构成的这个一维数组,二维数组首元素的地址也就是这个一维数组的地址。
数组的地址又用什么存储呢?用数组指针存储。
b、数组指针的使用
看下面这段代码及其输出结果:
可以看到,我们成功地用数组指针打印了二维数组,而其中最关键的一步在于这里:
这里有两个问题:
1、*(p+i)是什么意思?
p是一个一维数组的地址,因而p+i 进行的是数组地址的移动。p+0,就是不移动,还是该一维数组的地址;p+1,就是跳过一个一维数组,p+1即为下一个一维数组的地址。
对p+i解引用又怎么理解呢?p+i代表的是数组的地址,对其解引用,找到的自然是该地址所对应的数组。而实际上,我们用数组名代表整个数组,因此对 p+i 解引用得到的是一个一维数组的数组名。而数组名又是什么,是数组首元素的地址。
上述二维数组为arr,将其三个元素分别命名为arr1,arr2,arr3
所以,*(p+i)拿到一维数组,实质上是一维数组的数组名后,就正常进行一个一维数组的使用即可。
如果i=0,*(p+i)就是arr1, *(arr1+j) 也就是遍历了arr1这个一维数组中的元素。
i=1,i=2时以此类推
所以,基于上述理解,我们可以对上述代码作如下改写:
而这一切的变形,归根究底源于下式:
2、函数指针
A、什么是函数指针
函数指针,顾名思义,指向一个函数的指针
这里有三个问题:
1、Add与&Add有什么区别吗?二者没有什么区别,都是Add这个函数的地址
2、(*pf1)这个括号可以去掉吗?不可以,这又设计到优先级的问题。括号()的优先级高于*,如果不加括号,pf1会先与()结合,即进行函数调用,这样是不对的,因此必须加上(), (*pf1),让pf1先与 * 结合,确保pf1是一个指针变量(pf2同理)
3、函数的类型问题。定义变量时,去掉变量名即得变量类型。所以我们可以这么理解,去掉函数名,即得函数的类型,则上述Add函数的类型为 int (int x , int y) ,当然也可以写成int (int ,int) ,实际上注明函数形参的类型即可。
B、函数指针的使用
函数指针用于调用函数:
如上图所示,得到了我们想要的相加结果。
所以,对pf解引用,可以拿到函数Add,然后便可以如使用函数Add那般,使用*pf:*pf即为函数Add。
我们还可以对上述代码做如下改写,而不改变输出结果: