前言
hello,大家好,我在上一篇博客中分享了安得指针千万间,大庇天下地址具欢颜(上)篇,感谢大家的肯定与支持,今天我们来分享中篇,欢迎小伙伴们继续支持博主哦!
一、函数指针
什么是函数指针呢?
定义是这样的:函数指针是指向函数的指针
实际上函数指针就是用来存放函数的地址的。
我们来举一个例子:
我们之前讲到过,数组名就是数组元素的首地址,那么函数名是不是也是一个地址呢?
我们来验证一下:
通过这个例子我们就可以看出来,对于函数,取函数地址和函数名,获得的都是函数的地址。
那么函数指针具体怎么应用呢?我们来举一个简单的例子:
明确了函数指针,我们再来看下一个问题:
二、函数指针数组
我们知道指针数组是用来存放地址的,那么根据这个逻辑,我们便可以推出函数指针数组是用来存放函数指针的数组,事实上也是如此。
我们再来看一个函数指针数组的实例
#include<stdio.h>
int add(int x, int y)
{
return x + y;
}
int sub(int m, int n)
{
return m - n;
}
int main()
{
int a, b;
int *arr[2] = { &a, &b };//指针数组
int(*pf1)(int, int) = add;//函数指针
int(*pf2)(int, int) = sub;//函数指针
int(*pfarr[2])(int, int) = { add, sub };//函数指针数组
int(*pA[4])(int, int);//函数指针数组
return 0;
}
通过上面的代码的内容我们可以发现,虽然函数指针数组这个名字听起来十分不讲武德,但实际上还是非常中规中矩的。所以不要慌哦!
然后我们来看这样一个实现计算器的程序
#include<stdio.h>
void menu()
{
printf("欢迎使用计算器>>\n");
printf("*******************************\n");
printf("***** 1.add *******\n");
printf("***** 2.sub *******\n");
printf("***** 3.mul *******\n");
printf("***** 4.div *******\n");
printf("***** 0.exit *******\n");
printf("*******************************\n");
}
int add(int x, int y)
{
return x + y;
}
int sub(int x, int y)
{
return x-y;
}
int mul(int x, int y)
{
return x*y;
}
int div(int x, int y)
{
return x / y;
}
int main()
{
int input = 0;
int a = 0;
int b = 0;
int ret = 0;
do
{
menu();
printf("请选择>>\n");
scanf("%d", &input);
switch (input)
{
case 1:
printf("请输入两个操作数>>\n");
scanf("%d %d", &a, &b);
ret = add(a, b);
printf("%d\n", ret);
break;
case 2:
printf("请输入两个操作数>>\n");
scanf("%d %d", &a, &b);
ret = sub(a, b);
printf("%d\n", ret);
break;
case 3:
printf("请输入两个操作数>>\n");
scanf("%d %d", &a, &b);
ret = mul(a, b);
printf("%d\n", ret);
break;
case 4:
printf("请输入两个操作数>>\n");
scanf("%d %d", &a, &b);
ret = div(a, b);
printf("%d\n", ret);
break;
case 0:
printf("退出计算器>>\n");
break;
default:
printf("选择错误>>\n");
break;
}
} while (input);
return 0;
}
以上程序是我们通常的写法,如果我们利用函数指针数组,我们来看看这个程序应该是什么样子的:
#include<stdio.h>
void menu()
{
printf("欢迎使用计算器>>\n");
printf("*******************************\n");
printf("***** 1.add *******\n");
printf("***** 2.sub *******\n");
printf("***** 3.mul *******\n");
printf("***** 4.div *******\n");
printf("***** 0.exit *******\n");
printf("*******************************\n");
}
int add(int x, int y)
{
return x + y;
}
int sub(int x, int y)
{
return x - y;
}
int mul(int x, int y)
{
return x*y;
}
int div(int x, int y)
{
return x / y;
}
int main()
{
int input = 0;
int x = 0;
int y = 0;
int ret = 0;
int(*pf[])(int, int) = { 0, add, sub, mul, div };
do
{
menu();
printf("请选择>>\n");
scanf("%d", &input);
if (input == 0)
{
printf("退出程序>>\n");
break;
}
else if ((input >= 1) && (input <= 4))
{
printf("请输入两个操作数>>\n");
scanf("%d %d", &x, &y);
ret = pf[input](x, y);
printf("ret=%d\n", ret);
}
else
{
printf("输入错误,请重新选择>>\n");
}
} while (input);
return 0;
}
我们发现,利用函数指针数组之后的程序更简洁,更优美。如果有更多的函数,我们只需要在函数指针数组里增加新的元素就好,而不再需要去一个一个地增加case。这也是函数指针数组的一个优点。
好的,了解了函数指针数组之后,我们继续往下再了解一个东西:
三.指向函数指针数组的指针
我们通过上面的介绍知道,函数指针数组用来存放函数指针,那么函数指针数组数组名本身可不可以取地址呢?答案当然是可以的。我们来看一个例子:
#include<stdio.h>
int main()
{
int(*p)(int, int);//函数指针
int(*pp[4])(int, int);//函数指针数组
int(*(*ppp[4]))(int, int) = &pp;//指向函数指针数组的指针
return 0;
}
据以上的示例,相信你已经get到了指向函数指针数组的指针的概念了,我们在这里就不做过多的探讨了,我们往下看。
四.回调函数
听听这个小名字起滴,高深莫测与世隔绝的样子。
那么回调函数究竟是什么样子的呢?我们先来看看概念:
概念
回调函数就是一个通过函数指针调用的函数,如果你把函数指针作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数,回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另一方调用的,用于对该事件或条件进行响应。
好家伙,看完这一段话你是不是一脸懵圈?
反正我初见时是十分懵圈,但人生不只有初见,还有继续了解,所以莫慌,我们来深入了解一下回调函数:
举例
首先,我们举一个例子:
我们来看上面的那个程序里的test1,它的参数放的是test2,而test2函数里的参数是一个函数指针,在这个程序里,我们并没有直接调用test1,而是把test1的地址传递给了另一个函数test2,然后我们通过p反过来去调用了test1函数,我们称test1函数为回调函数。这个程序可以很好地演示回调函数,当然,你的内心可能任然会有一些迷茫,我们再来用计算器的程序来演示一下:
#include<stdio.h>
void menu()
{
printf("欢迎使用计算器>>\n");
printf("*******************************\n");
printf("***** 1.add *******\n");
printf("***** 2.sub *******\n");
printf("***** 3.mul *******\n");
printf("***** 4.div *******\n");
printf("***** 0.exit *******\n");
printf("*******************************\n");
}
int add(int x, int y)
{
return x + y;
}
int sub(int x, int y)
{
return x - y;
}
int mul(int x, int y)
{
return x*y;
}
int div(int x, int y)
{
return x / y;
}
void cal(int(*p)(int,int))
{
int a = 0;
int b = 0;
int ret = 0;
printf("请输入两个操作数>>\n");
scanf("%d %d", &a, &b);
ret = p(a, b);
printf("ret=%d\n", ret);
}
int main()
{
int input = 0;
do
{
menu();
printf("请选择>>\n");
scanf("%d", &input);
switch (input)
{
case 1:
cal(add);
break;
case 2:
cal(sub);
break;
case 3:
cal(mul);
break;
case 4:
cal(div);
break;
case 0:
printf("退出计算器>>\n");
break;
default:
printf("选择错误>>\n");
break;
}
} while (input);
return 0;
}
相对于原程序,我们增加了cal函数,我们在程序中,将加减乘除的函数名作为cal的参数,从而避免了使用多次重复打相同的代码,怎么样?你有没有体会到回调函数的强大功能?
说成大白话,回调函数就是,我们把一个函数的地址传递给另外一个函数,而另外一个函数又在某种情况下主动去调用指针所指向的函数。
总结
好滴,我们今天的分享就先到这里,如有错误,还有大家批评指正,我们接下来还会分享《安得指针千万间,大庇天下地址具欢颜(下)》篇,欢迎大家继续支持哦!!!在下篇里干货慢慢哦。