【指针的进阶(3)】回调函数和qsort排序各种类型的数据


前言

前两章讲了指针的类型,数组传参和指针传参,还有函数指针和函数指针数组,接下来第三章讲回调函数


指针函数非常大的用途就是实现回调函数

一、回调函数是什么?

回调函数就是通过函数指针调用的函数。

如果把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

如何实现回调函数

下面的代码过于冗余
如果分装一个函数,调用它,能大大减少了敲代码工作量,这个函数就是回调函数
在这里插入图片描述
所以分装一个函数叫Calc,在使用加减乘除时调用这个函数

        case 1:
			Calc(Add);
			break;
		case 2:
			Calc(Sub);
			break;
		case 3:
			Calc(Mul);
			break;
		case 4:
			Calc(Div);
			break;

函数地址传给Calc函数,用函数指针接收
这个pf是函数指针,指向的参数是(int, int),返回类型是int

void Calc(int (*pf)(int, int))

具体代码如下

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");
}
void Calc(int (*pf)(int, int))
{
    
                 
	int x = 0;
	int y = 0;
	int ret = 0;
	printf("请输入两个操作数:");
	scanf("%d %d", &x, &y);
	ret = pf(x, y);
	printf("ret = %d\n", ret);
}
int main()
{
    
    
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 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;
}

过程如下:
在这里插入图片描述

不是直接调用Add函数,而是把Add函数传给pf指针函数,,通过pf函数指针去调用Add函数,实现计算,那么pf函数指针就是回调函数 。

作用: 回调函数更具有广泛性和通用性,代码不易写死,如果直接调用Add函数,代码就被固定住了,要想调用别的函数,那个代码就不适用了。

二、回调函数的应用——qsort

qsort 是标准库里的函数,用来排序

qsort函数怎么实现回调函数呢?

这就得说到冒泡排序了
一般冒泡排序是这样写的:

int main()
{
    
    
	int arr[10] = {
    
     9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	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;
			}
		}
	}
	for (i = 0; i < sz; i++)
	{
    
    
		printf("%d ", arr[i]);
	}
}

这如果要排结构体,浮点型等其他类型的数据呢,这个代码就存在一定的问题了,只适用于整型的排序,形式固定住了,不灵活不广泛。

那有没有一种写一个排序函数,适用于任何类型呢?
qsort函数就能解决这个问题

qsort排序各种类型的数据

qsort函数的特点
1.快速排序的方法
2.适合于任意类型数据的排序

  1. 在cplusplus网站上搜索 qsort,可以看到qsort有四个参数:

在这里插入图片描述
四个函数的意思是:
void qsort(
void* base,//指向需要排序的数组的第一个元素
size_t num,//排序的元素个数
size_t size,//一个元素的大小,单位是字节
int(*cmp)(const void*, const void*)
);//函数指针类型-这个函数指针指向的函数,能够比较base指向数组中的两个元素


                      用qsort排序整型

根据上面四个参数,写出下面这段代码
在这里插入图片描述

  1. 最后一个参数空出来没有写,是因为这里涉及到一些重要的知识点
    第四个参数是要写一个函数,能够比较base指向数组中的两个元素,并把结果返回。 那么就写一个函数叫cmp_int,参数就是cplusplus网站上qsort函数给的两个参数在这里插入图片描述
    代码如下:
int cmp_int(const void* p1, const void* p2)
  1. 写完了函数,怎么比较两个数呢?
    在cplusplus网站上找到计算方法如下:
    在这里插入图片描述
    当p1指向的值小于p2指向的值时,返回小于0的数字;
    当p1指向的值等于p2指向的值时,返回0;
    当p1指向的值大于p2指向的值时,返回大于0的数字;

那么就让p1p2两个数作差,把结果返回去。

但这里还涉及到一些知识:
一个热知识::void* 的指针是无具体类型的指针。
void* 类型的指针可以接收任意类型的地址

在函数参数这里用void* 的好处就是,广泛性,什么类型的数据排序都能接收,编程不会报警告。
但这种类型的指针不能直接解引用,也不能直接进行指针运算。所以在比较两个数大小时,需要强制类型转换。
意思就是要排序什么类型,就强制类型转换什么类型
比如:当要比较整型数据时,p1和p2要强制类型转换成整型,当比较浮点型数据时,p1和p2强制类型转换成浮点型。

代码如下:

int cmp_int(const void* p1, const void* p2)
{
    
    
	return (*(int*)p1 - *(int*)p2);
}
  1. 最后把结果打印出来
    分装一个print函数
    参数是整型数组和数组大小
    循环打印每个元素
void print(int arr[], int sz)
{
    
    
	int i = 0;
	for (i = 0; i < sz; i++)
	{
    
    
		printf("%d ", arr[i]);
	}
}

完整代码如下:

#include<stdlib.h>
int cmp_int(const void* p1, const void* p2)
{
    
    
	return (*(int*)p1 - *(int*)p2);
}
void print(int arr[], int sz)
{
    
    
	int i = 0;
	for (i = 0; i < sz; i++)
	{
    
    
		printf("%d ", arr[i]);
	}
}
test1()
{
    
    
	int arr[10] = {
    
    9,8,7,6,5,4,3,2,1,0};
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr,sz,sizeof(arr[0]),cmp_int);
	print(arr, sz);
}
int main()
{
    
    
	test1();
	return 0;
}

注意:qsort 函数的头文件是 #include<stdlib.h>


                     用qsort 排序结构体

年龄比较大小

#include<stdio.h>
#include<stdlib.h>
struct Stu
{
    
    
	char name[20];
	int age;
};
int cmp_stu_by_age(const void* p1, const void* p2)
{
    
    
	return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}

void print(struct Stu arr[], int sz)
{
    
    
	int i = 0;
	for (i = 0; i < sz; i++)
	{
    
    
		printf("%d ", arr[i].age);
	}
}
void test2()
{
    
    
	struct Stu arr[] = {
    
     {
    
    "zhangsan",20},{
    
    "lisi",50},{
    
    "wangwu",15} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
	print(arr,sz);
}
int main()
{
    
    
	test2();
	return 0;
}

姓名比较大小

#include<stdio.h>
#include<stdlib.h>
struct Stu

{
    
    
	char name[20];
	int age;
};

int cmp_str_stu_by_name(const void* p1, const void* p2)

{
    
    
	return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}
//名字比较不能相减,名字是字符串,字符串比较大小用strcmp

void print(struct Stu arr[], int sz)
{
    
    
	int i = 0;
	for (i = 0; i < sz; i++)
	{
    
    
		printf("%s ", arr[i].name);
	}
}
void test3()

{
    
    
	struct Stu arr[] = {
    
     {
    
    "zhangsan",50},{
    
    "lisi",15},{
    
    "wangwu",30} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_str_stu_by_name);
	print(arr, sz);
}

总结

本章讲了回调函数的含义如何实现回调函数qsort排序各种类型的数据的内容,希望对您有帮助!

猜你喜欢

转载自blog.csdn.net/2301_76496134/article/details/131809245