C语言:函数指针,从懵逼到精通
我们知道,指针是一个地址,C语言中想要取出地址就需要用到&.
int a = 0;取整形指针出来就是 &a;
int arr[] = {0};取数组指针出来就是&arr;
而函数指针也不例外
创建了函数: int fx(int a,int b);
则把函数地址取出来就应该是&fx
那么这里还需要引入一个知识
我们知道arr数组名=数组首元素地址
因此我们知道:arr不加&也是一个地址(数组首元素地址)
但是arr != &arr
(1条消息) C语言:数组指针_srhqwe的博客-CSDN博客
那么函数名呢?
所以这里我们可以看到
fx == &fx
所以我们就知道:
①数组名 != &数组名
②函数名 == &函数名
讨论完函数的地址,则就需要引入存放函数地址的指针了
为了方便观察
创建一个数组以及一个函数
int arr[10];
int fx(int a,int b);
结合数组指针的知识,我们可以知道
(*p)表示这是一个指针
函数名():表示一个函数
数组名[]: 表示一个数组
| |
| |
(*p)[]表示的是指向数组的指针
[]表示数组这是我们知道的,那么()就应该表示函数
(*p)()就表示的是指向函数的指针
同样地,我们知道,数组的类型,就是数组指针的类型
int (*p)[]
那么也可以推出,函数的类型,就是函数指针的类型
int (*p)()
而我们还知道,数组指针[]中,需要写上原数组[]中的参数
int (*p)[10];那么此时就是一个数组指针
| |
那么在函数指针()中,应该也需要填上,原函数()中的参数。
但是,这里并不需要填写完整,只需要填写,原函数()中的类型,那么也就是:
int (*p)(int,int);那么此时就是一个函数指针
只需要再给这个函数指针,附上一个地址,那么此时就是函数指针变量了,
int (*p)(int,int) = &fx;
有&,那就一定有*
所以我们可以尝试使用*来解引用一下这个函数指针.
*p就是解引用,那么*p解引用出来就是fx这个函数,而我们正常使用fx这个函数是fx(参数),所以:
(*p)(参数)就可以调用这个函数了.
(补充:(*p)将*p用括号包起来,是因为括号优先级比*高,所以如果不用括号抱起来,就会使后面的括号先进行,那么结果就会是:这个函数调用后,返回的结果,再使用*解引用了)
调用:
那么,我们上面说了,所以还知道:函数名 == &函数名 | 所以我们改成: int (*p)(int, int) = fx;
我们还知道p是指向fx的指针
所以p == fx ==&fx
所以我们还可以将(*p)(1, 2)中的(*p)改为p
也就是p(1, 2)
所以再给函数指针解引用时,*可以省略
无论多少个*都会省略
==============================================================
以上是初步了解函数指针,接下来,下面的内容则是深入了解.
==============================================================
①(*(void (*)())0)()
(*(void (*)())0)()中,将(void (*)()去掉,就是(*0)(),在这里,0就很难理解,其实在这里0是地址,0x00000000
这里0也可以是别的地址,如:0x11223344
所以这里就是将0这个地址进行*解引用,然后调用这个函数
此时我们可以发现,0处的地址不一定是一个函数,可是从(*0)()这里又知道,因为有(),所以这里一定是函数,所以为了保证0处是函数,所以需要给0进行强制类型转换,转换成函数指针类型
所以这里的void (*)()就是函数指针类型,这与数组指针类型类似:void (*)[]
因此最后得到了:(*(void (*)())0)()
②void(*signal(int,void (*)(int,int)))(int)
这里又可以把,中间的:signal(int,void (*)(int,int))去掉看看,得到了void(*)(int),所以这又是函数指针类型.
那么可以讨论一下signal(int,void (*)(int,int)),signal是一个函数,用于警报的一个C语言函数。
其中signal有两个参数:
int类型
函数指针类型
因为signal函数前面有个*,所以返回的是一个指针(函数指针)
所以,有一个signal的函数,参数一个是int类型,一个是函数指针类型,这个函数返回的是一个指针,这个指针也是函数指针。
为了方便理解,可以这样看这个函数(ps:只是方便理解,不能这样书写!)
我们可以用typedef定义类型
那么为什么把名字写在(*)里呢?
我们知道,定义函数指针变量和数组指针变量的时候是:
int (*p)();
int (*p)[];
我们知道,变量名都是写在(*)里面
正常定义整形类型,就是:int p;
所以如上图所示,在定义函数指针变量和数组指针变量的时候,都是在(*)里定义
如果要理解起来就是:int (*)() p ; 只是将p写到了(*)里
回归正题,我们定义了函数指针变量为pfx,为了方便理解void(*signal(int,void (*)(int,int)))(int)
完全可以将void(*signal(int,void (*)(int,int)))(int) 改变为:
此时我们就可以很好地理解这个函数了
PS:函数定义的时候,可以不需要参数名!
二.函数指针数组
了解完了函数指针,来看看函数指针数组。我们知道数组指针数组是int (*arr[5])[5],因此我们可以推出,函数指针数组是int (*arr[5])(int)。
函数指针数组的实际应用:
三.函数指针数组的地址
函数指针数组的地址,我们应该怎么取出来呢?放在什么样的变量里呢?
想要得到 函数指针数组的地址 我们肯定需要定义函数,函数指针和函数指针数组
函数: ①int t1(int) ②int t2(int) ③int t3(int)
函数指针:①int (*p1)(int) = &t1 ②int (*p2)(int) = &t2 ③int (*p3)(int) = &t3
函数指针数组:int (*pa[3])(int) = {p1,p2,p3}
函数指针数组的地址,我们知道他是一个数组的地址也就是数组指针,类型是函数指针类型
所以我们可以创建一个数组指针,而数组指针对应的数组是有三个元素:int (*arr)[3]
我们还知道,他是一个函数指针类型,所以得到了:int (*(*arr)[3])(int)
如果有错,希望纠错!