C语言函数指针
一、什么是函数指针
1.理解函数指针
函数指针,首先根据他的名称我们就大概可以了解到他是一个指针,是什么样的指针呢?是和函数有关的一种特殊的指针。
我们知道 int*p 是指向int型变量的指针,char*p是指向char型变量的指针,那么不难理解,函数指针就是一个指向函数的指针。
int arr[10]={
0};
int *p = arr;
我们知道,在数组中数组名就是首元素的地址。
那么试着猜猜看,函数的地址是什么样的情况呢。
我们不妨打印一下看看结果是什么样的。
int add(int x, int y)
{
return x + y;
}
int main()
{
printf("%p\n",&add);
printf("%p\n",add);
return 0;
}
结果如下:
结论:函数的函数名就是函数的地址。
2.如何定义函数指针以及使用
搞清楚了函数的地址,和函数指针的作用,那么如何去定义一个 函数指针呢?
首先,函数有三大要素:函数名、函数参数、函数返回类型、函数指针是一个指针,所以首先我们先保证函数名先和*号结合,因为在c语言运算符的运算顺序中,[]的优先级高于*,所以我们使用()来解决这个问题:(*pf),之后我们考虑add函数的参数,int x,int y,是两个整型参数,所以我们可以写出来 (*add)(int int),返回类型是 int型,所以完善后我们得到:
int (*pf)(int int)=add;
我们可以得到一个定义函数指针的格式:返回类型 (*指针变量名)(函数参数类型)=函数名;
那么定义好一个函数指针,该如何通过指针访问的方式去调用函数呢?
我们知道,想要通过指针访问元素内容,需要通过*来解引用访问,由此推断可以这样使用函数指针:(*pf)(x,y)。
但是其实在函数指针调用时,pf左边这个*号并无实际的意义,加或不加对结果都不会有影响,这使我们使用函数指针更加方便 :pf(x,y);便可以使用add函数。
3.加深理解函数指针
理解了函数指针的概念后,我们通过两段复杂的函数指针代码来加深理解。
//代码1:
(*void(*)()0)();
//代码2:
void(*signal(int,void(*)(int)))(int);
这两段代码摘自《C缺陷和陷阱》
第一眼看到两段代码,头痛欲裂,无从下手,想要理解这两段代码,需要先理解类型的概念:
我们知道:
char*pf;
// 指针变量pf的类型是char*
int *pi;
// 指针变量pi的类型是int*
int(*parr)[10];
// parr是一个数组指针,其类型是int(*)[10];
// 根据上述例子,我们不难得出,指针变量的类型就是将指针变量名去掉后剩余的部分
void(*pf)();
// 我们去掉指针变量名pf可以得到 void(*)()就是这个函数指针的类型
那我们就不难理解第一行代码了:
- 首先(*某类型 0)是将0强制类型转化为了某类型,这串代码中是将0强转为void(*)( )类型的一个函数指针。
- 其次 外边的括号其实是通过函数指针对函数的一次调用,解引用0地址,去0地址处的这个函数,被调用的函数是无参,返回类型是void。
那我们来看第二段代码:
-
根据上面的规则void(*)(int)是一个函数指针的类型。
-
函数名为signal,参数有两个一个是int型参数,另一个则是void(*)(int)类型的函数指针。
-
那么最外层的一定是指针函数的返回类型:void(*)(int)。
-
所以这段代码其实是函数指针的一次声明:signal函数有两个参数,第一个是int类型,第二个是void()(int)的函数,返回值的类型是void()(int)函数指针类型。
这么看来其实这两段代码也不过如此,我们在分析这种复杂函数指针代码的时候,只要抓住三大要素,函数的函数名,参数以及返回值,那所有的问题其实都不复杂。
二、函数指针数组
1.如何理解函数指针数组
数组是存放相同类型数据的空间,我们知道指针数组可以用来存放多个指针,例如:
int* arr[5];
把多个函数的地址存放到一个数组里,那么这个数组就是函数指针数组: