前言
在指针进阶系列里已经介绍了字符指针、数组指针、指针数组、数组参数、指针参数,本篇将介绍指针进阶的最后一部分-函数指针。
1.函数指针
先看下面的代码
//函数指针
int Add(int x, int y)
{
return x + y;
}
int main()
{
int arr[10] = { 0 };
int (*parr)[10] = &arr;//取出数组的地址
//parr是指向数组的指针,存放的的是数组的地址
//函数指针:存放函数地址的指针
printf(" &Add:%p\n", &Add);
printf(" Add:%p\n", Add);
//数组名 != &数组名
//函数名 == &函数名 == 函数的地址
int (*pf)(int, int) = &Add;
int (*pf)(int, int) = Add;//Add === pf
//pf就是一个函数指针变量
//函数指针的调用:
int ret = (*pf)(4, 4);//1
int ret = pf(3, 3);//2
int ret = Add(2, 2);//3
int ret = (********pf)(5, 5);
//以上三种方式一模一样,在函数指针里,(*pf)里的*只是一个摆设,没有意义,写成(***pf)也一样,注意只有在函数指针调用里才是这样
//但不能写成int ret= * pf(2,2);
printf("%d\n", ret);
return 0;
}
根据上面的结果可知,函数指针是存放函数地址的指针,函数名就是函数的地址,与数组名不同的是,数组名 != &数组名,而函数名等价于&函数名,所以函数名 == &函数名 == 函数的地址。函数指针的调用有很多方式,可以使用一个函数指针变量int (*pf)(int, int) = &Add 这样调用pf和调用Add都是一样的,特殊的是( * pf)的 * 只是一个摆设,没有意义,写成(***pf)也一样,这里的括号不可以省略,注意只有在函数指针调用里才是这样。
2.函数指针数组
//函数指针数组 - 存放函数指针的数组
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int main()
{
int (*pf1)(int, int) = Add;
int (*pf2)(int, int) = Sub;
int (*pfarr[2])(int,int) = { Add,Sub };//pfarr就是函数指针数组 同类型的函数
int (*pfarr[2])(int, int) = { pf1,pf2 };
int(*pfarr[3])(int,int) = { NULL,Add,Sub };//pfarr[1]==Add;pfarr[2]==Sub;
return 0;
}
函数指针数组是用来存放函数指针的数组,要求必须是同类型的函数,可以存放NULL空指针。为什么pfarr是一个函数指针数组呢?int(pfarr[3])(int,int):pfarr先于[]结合,说明pfarr是一个数组,数组的每一个元素是int()(int,int),即数组的每一个元素都是函数指针,这就是函数指针数组的定义方式。
3.指向函数指针数组的指针
指向函数指针数组的指针
int (*p1)(int, int);//函数指针
int (* p2[3])(int, int);//函数指针数组
int (* (*p3)[3])(int, int) = &p2;//取出的是函数指针数组的地址
p3就是一个指向【函数指针数组】的指针
如何一步步分清变量的类型: 先看一个例子int arr[2];他的数组类型是int[2],每个数组元素类型是int。
再来看int (* (p3)[3])(int, int),p3先于结合,说明是一个指针,(*p3)再与[]结合,说明是一个数组指针,数组的每一个元素类型是int( * )(int,int),即函数指针,所以p3是一个存放函数指针数组的指针。
4.回调函数
回调函数定义:一个通过函数指针调用的函数
回调函数的使用如下:
//回调函数 - 一个通过函数指针调用的函数
int Calc(int (*pf)(int, int))//Calc函数就是一个回调函数
{
int x, y;
scanf("%d %d", &x, &y);
return pf(x, y);
}
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int main()
{
int ret = 0;
ret = Calc(Add);
printf("%d\n", ret);
ret = Calc(Sub);
printf("%d\n", ret);
return 0;
}
通过函数名传参给回调函数,回调函数使用函数指针作为形参接收,然后回调函数就可以使用函数指针来调用这个函数。
总结
到这里函数指针就结束了,来两个抽象的题~.~
(*(void (*)())0)();
解释如下:
()0 强制类型转换,这里()内是函数地址类型,是把函数地址强制转换为0 地址
实际上是调用0地址处的函数
该参数无参,返回类型为void
void (*)() - 函数指针类型
(void (*)())0 - 对0进行强制类型转换,被解释为一个函数地址
* (void (*)())0 - 对0地址进行了解引用操作
(*(void (*)())0)() - 调用0地址处的函数
2.
void (*signal(int, void (*)(int)))(int);
解释如下:
void(*)(int) signal(int, void (*)(int));//实际上不能这样编写
typedef void(*pfun_t)(int); - 对void (*)()的函数指针类型重命名为pfun_t
也就等价于 pfun_t signal(int,pfun_t);
1. signal先于()结合 - 说明signal是一个函数名
2. signal函数的第一个参数类型是int ,第二个参数类型是函数指针
该函数指针,指向一个参数为int,返回类型为void的函数
3. signal函数的返回类型也是一个函数指针
该函数指针,指向一个参数为int,返回类型为void的函数
signal是一个函数声明
能把上面两个抽象题搞清楚,那么函数指针应该就很清楚了。
到这里指针进阶系列就结束了。