yo!这里是回调函数

目录

前言

函数指针介绍

1.概念

2.用法举例

回调函数概念介绍

回调函数用途举例

用途举例一:计算器程序实现

1.普通实现

2.借助回调函数实现

3.对比与总结 

用途举例二:库函数qsort

1.qsort介绍

2.qsort用法举例

3.模拟实现qsort


前言

        什么是回调函数?我想大家应该都有所耳闻,但是不知道它具体的概念,用法以及为啥用,今天,我就在这篇文章中好好介绍一下,也通过一些栗子来加深大家的理解,首先呢,要说明白回调函数,就要先说一下函数指针,跟我来看看吧。

函数指针介绍

1.概念

        整型指针存放整型地址的指针变量,字符指针存放字符地址的指针变量,所以,函数指针,顾名思义,存放函数地址的指针变量。语法:函数返回类型  (*指针变量名)(函数参数列表)

2.用法举例

前提:

        有如下两个函数Add、Sub

int Add(int x, int y)
{
	return x + y;
}

初始化:

        pf为变量名,除去之后int (*)(int, int) 为函数指针类型,代表pf为函数指针变量,函数名或者&函数名都可以用来初始化函数指针

int (*pf)(int, int) = Add;
//或
int (*pf)(int, int) = &Add;

使用:

        pf为变量名,带不带解引用符都可以调用函数

int tmp = pf(2, 3);
//或
int tmp = (*pf)(2, 3);

        那就有人问了,好端端的一个函数,明明可以直接调用它,为啥要通过存入函数指针呢?这就狭隘了,存在即合理,下面即将介绍的回调函数就是它的典型应用之一。

回调函数概念介绍

        回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。---百度百科

        举个例子简单理解一下,有函数int Add(int x,int y),void test(int (*pf)(int ,int)),当Add函数通过test函数的函数指针参数传入test函数内,此时Add函数就叫做回调函数。

回调函数用途举例

  • 用途举例一:计算器程序实现

1.普通实现

        对于计算器程序的实现,大家应该都可以得心应手,但是在认识回调函数之前,可能只能写出下面的小白写法,这里只实现计算器的基本的加减乘除功能以说明问题。

代码:

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 menu()   //菜单
{
	printf("**************************\n");
	printf("****  1.add   2.sub   ****\n");
	printf("****  3.mul   4.div   ****\n");
	printf("****  0.exit          ****\n");
	printf("**************************\n");
}


int main()
{
	int input = 0;
	int x = 0, y = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请输入选项:\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入两个操作数:\n");
			scanf("%d %d", &x, &y);
			ret = Add(x, y);
			printf("结果为%d\n", ret);
			break;
		case 2:
			printf("请输入两个操作数:\n");
			scanf("%d %d", &x, &y);
			ret = Sub(x, y);
			printf("结果为%d\n", ret);
			break;
		case 3:
			printf("请输入两个操作数:\n");
			scanf("%d %d", &x, &y);
			ret = Mul(x, y);
			printf("结果为%d\n", ret);
			break;
		case 4:
			printf("请输入两个操作数:\n");
			scanf("%d %d", &x, &y);
			ret = Div(x, y);
			printf("结果为%d\n", ret);
			break;
		case 0:
			printf("退出成功\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);

	return 0;
}

2.借助回调函数实现

        在上方的小白写法可以看出,部分代码出现多次,很冗余,那么有什么方法可以减少这种冗余呢?从switch语句中的不同case可以看出:大部分case的代码是相同的,只有要实现的函数不同。

        所以,通过函数指针的学习,我们可以去定义这样一个函数(calc),参数设为函数指针类型,通过调用不同功能的函数实现相应功能,以减少代码冗余。

代码:

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 menu()
{
	printf("*****************************\n");
	printf("*****  1.加法   2.减法  *****\n");
	printf("*****  3.乘法   4.除法  *****\n");
	printf("*****  0.退出           *****\n");
	printf("*****************************\n");

}

//接受不同函数以实现对应功能的函数,此时上面的Add,Sub等函数称为回调函数
void calc(int (*pf)(int,int))
{
	int x = 0;
	int y = 0;
	int tmp = 0;

	printf("请输入2个操作数:>");
	scanf("%d%d", &x, &y);
	tmp = pf(x, y);
	printf("结果为%d\n", tmp);
}

int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			calc(Add);
			break;
		case 2:
			calc(Sub);
			break;
		case 3:
			calc(Mul);
			break;
		case 4:
			calc(Div);
			break;
		case 0:
			printf("退出成功\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);

	return 0;
}

3.对比与总结 

        先看普通写法,代码重复很多,很冗余,而且这里我们实现的功能少,所以还不算特别多,但如果加上计算器的其他功能,代码冗余量可想而知,反观借助回调函数实现的程序,通过一个函数的参数使用函数指针来调用不同函数,这大大的减少了代码的冗余量。

        这里使用回调函数是一个什么情况呢?总的来说,就是当主函数需要实现多个不同但相似的功能函数时,只要找出这些函数的共性,去实现一个函数,参数中定义一个函数指针,可以调用这些函数,此时就不用去单个实现这些函数了。

  • 用途举例二:库函数qsort

1.qsort介绍

        我们学过有冒泡排序,堆排序,快速排序,简单选择排序等排序方法,这里举冒泡排序算法,算法如下:

void bubble_sort(int arr[], int sz)
{
    int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		for (j = 0; j < sz-1-i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}

        但是,我们发现这些排序算法只能排序整型变量,对于浮点型,字符串,结构体等一些数据类型无法排序,所以有无一种函数适用于所有数据类型的排序呢?有的

        qsort就满足了这种需求,它是一种基于快速排序的适用所有数据类型排序的库函数,先来看看有关它的定义。

语法与头文件:

qsort参数列表 :

参数中的函数指针所指向的函数类型: 

        cmp是个函数指针类型,就是存放需适应多种数据类型比较的函数地址,以调用需比较的数据类型的函数,比如,需要比较整型,那就传入适用于比较整型大小的函数,见用法举例①,需要比较结构体,就传入适用于比较结构体大小的函数,进一步按照结构体的哪一个成员比较就看实际情况了,见用法举例②。

        void* 可以接受任意数据类型的指针,但无法直接解引用,需要强转,这就可以适应任何数据类型的传入。

2.qsort用法举例

        ①比较整型

//自定义函数,用来排序整型数组,默认升序,若需要降序,只需要改为e2-e1
int cmp_int(const void* e1,const void* e2)   
{
	return *((int*)e1) - *((int*)e2);   //通过强转来获取所比较数据类型大小的权限
}

int main()
{
	int i = 0;
	int arr[] = { 5,8,9,6,7,3,1,4,2,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	
	qsort(arr, sz, sizeof(arr[0]), cmp_int);

	//打印数组
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

测试:

        ②:比较结构体

//自定义函数,比较结构体类型,按照成员名字来比较,当然也可以设置比较成员年龄或分数来比较
int cmp_struct_name(const void* e1, const void* e2)
{
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
    //先强转成要比较的结构体类型,再访问其成员
}

int main()
{
	int i = 0;
	struct Stu arr[] = { {"Mary",18,85.5},{"Xiao",21,95.0},{"Army",20,90.0} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	
	qsort(arr, sz, sizeof(arr[0]), cmp_struct_name);

	//打印数组
	for (i = 0; i < sz; i++)
	{
		printf("%s ",arr[i].name);
	}
	return 0;
}

测试:

3.模拟实现qsort

        qsort基于快排实现的库函数,我们基于上面的冒泡排序模拟实现同样功能的bsort函数。

思路过程:

        bsort函数大部分与冒泡排序函数相同,只有交换两个元素部分不同,重点讲解。

        已知bsort函数传入首地址void* 类型,元素个数num,以及元素大小width,我们无法访问所需要比较的数据类型大小的完整数据,但我们是可以将void*强转成char*类型,一个字节一个字节访问,一个字节一个字节交换,而width就是我们交换的次数,循环一轮就相当于交换了两个元素,见下图,比如交换9,8,小端字节存储

代码:

void bsort(void* base, int num, int width, int(*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	for (i = 0; i < num - 1; i++)
	{
		int j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
            //交换两个元素
			char* pc1 = (char*)base + j * width;  //元素首字节地址
			char* pc2 = (char*)base + (j + 1) * width;   //另一元素首字节地址
			if (cmp(pc1,pc2)>0 )   //传入自定义函数中比较大小
			{
				int t = 0;
				for (t = 0; t < width; t++)
				{
					char tmp = *pc1;
					*pc1 = *pc2;
					*pc2 = tmp;
					pc1++;
					pc2++;
				}
			}

		}
	}
}

int main()
{
	int i = 0;
	int arr[] = { 5,8,9,6,7,3,1,4,2,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);

	bsort(arr, sz, sizeof(arr[0]), cmp_int);

	//打印数组
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

测试:

猜你喜欢

转载自blog.csdn.net/phangx/article/details/130537328
今日推荐