前言

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的参数,从而避免了使用多次重复打相同的代码,怎么样?你有没有体会到回调函数的强大功能?

说成大白话,回调函数就是,我们把一个函数的地址传递给另外一个函数,而另外一个函数又在某种情况下主动去调用指针所指向的函数。

总结

好滴,我们今天的分享就先到这里,如有错误,还有大家批评指正,我们接下来还会分享《安得指针千万间,大庇天下地址具欢颜(下)》篇,欢迎大家继续支持哦!!!在下篇里干货慢慢哦。