1. 字符指针变量
通过已知的知识,我们知道字符变量的地址可以这么存储
当然,还有另一种书写方式
2. 数组指针变量
区别与指针数组(存放指针的数组),数组指针是指向数组的指针,就如同整形指针是指向整形的指针,字符指针是指向字符的指针,它们都是指针变量
下标引用(变址)操作符结合性高于解引用操作符,所以在定义 指针数组 和 数组指针 的时候是有很大区别的
我们尝试存储一下数组的地址
值得注意的是,如果粗暴的用数组指针来操作一维数组是很麻烦的:
所以数组指针多用于操作二维数组。
3. 二维数组传参的本质
首先我们回忆一下一维数组传参的内容,在形参部分我们有两种写法:1.写成数组的形式接收 2.写成指针的形式接收
在扫雷程序中我们在二维数组的传参过程中是用到了 写法1:二维数组的形式来接收二维数组,那么二维数组传参有没有办法写成指针的形式接收
4. 函数指针变量
4.1 函数指针变量的创建
如同整形指针,字符指针,数组指针,函数指针变量是用来存放函数的地址的
我们尝试打印观察一下函数的地址
数组名----是指向数组首元素的地址 &数组名----是指向整个数组的地址
与其不同的是, 函数名 和 &函数名 是完全等价的
下面我们尝试创建并使用一个函数指针变量
在定义函数变量的时候首先声明这是一个指针
*pf
然后声明这个函数形参的类型,但由于结合顺序的愿意要给指针打上括号
(*pf)(int, int)
最后声明函数的返回值
int (*pf)(int, int)
如此便完成了一个函数指针的定义
4.2 两段有意思的代码
这段代码是一次函数调用
1. 把0这个整形变量强制类型转换成一个函数指针,这个函数没有参数,没有返回值
2. 然后调用0地址处的函数
详解:
首先可以看出来 void (*)() 是一个函数指针类型(不是函数指针变量)
然后 (void (*)())0 将 (int)0 强制转换成这个函数指针类型,所以说现在0从一个整形变量成为了一个指针变量
最后 (*(void (*)())0)() 解引用 0 地址处的这个函数再传参void,相当于调用了这个函数
这段代码是一次函数声明
首先函数名叫 signal
函数有两个参数,类型分别是 int 和 void(*)(int)
函数返回值是 void (*)(int) 这种类型函数指针
4.3 typedef关键字
typedef是用来类型重名的,可以将复杂的类型简单化
如果你觉得unsigned int写起来太复杂,你可以再主函数开始前写上
typedef unsigned int uint;
这样之后就可以只写uint来定义无符号整形变量
数组指针 和 函数指针 也可以这样操作,但过程有些不同,要将改后的类型名放在括号里
既然已经了解了如何简化类型名,那我尝试简化一下刚才的声明
5. 函数指针数组
把函数的地址放在一个指针里,那这个数组就叫函数指针数组
我在这段代码中写了加减乘除4中函数(add,sub,mul,div),又用了两种方式书写函数指针数组,第一种是粗暴的定义一个数组和数组指向的元素,第二种是用typedef关键字自己定义好函数类型的类型名,然后再定义数组及其指向元素,我认为第二种定义方式更加直观。
然后我通过访问数组元素获取函数地址,再输入参数进行函数运算,并打印运算结果。
在访问数组元素调用函数时我也用到了两种方法,第二种比较直观,我就不过多赘述了,直接解释第一种:
*(pfarr + i)找到数组元素,也就是函数地址,此时其实已经可以直接输入参数进行函数操作了,但是要注意加括号 (*(pfarr + i))(参数,参数),因为如果不加括号的话参数的括号结合顺序是要比 * 高的,那算出来的就不是我们想要的函数了,当然你注意到我在 (*(pfarr + i)) 的基础上又加了一层解引用变成 (*(*(pfarr + i))) ,根据前文讲解的知识可以知道这其实没什么用,就是这么写,会,额,emmm,昂,麻烦一点。
6. 转移表
将函数地址储存在数组之中,通过数组元素调用函数的方法被叫做转移表