前言
前两章讲了指针的类型,数组传参和指针传参,还有函数指针和函数指针数组,接下来第三章讲回调函数
指针函数非常大的用途就是实现回调函数
一、回调函数是什么?
回调函数就是通过函数指针调用的函数。
如果把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
如何实现回调函数
下面的代码过于冗余
如果分装一个函数,调用它,能大大减少了敲代码工作量,这个函数就是回调函数
所以分装一个函数叫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.适合于任意类型数据的排序
- 在cplusplus网站上搜索 qsort,可以看到qsort有四个参数:
四个函数的意思是:
void qsort(
void* base
,//指向需要排序的数组的第一个元素
size_t num
,//排序的元素个数
size_t size
,//一个元素的大小,单位是字节
int(*cmp)(const void*, const void*)
);//函数指针类型-这个函数指针指向的函数,能够比较base指向数组中的两个元素
用qsort排序整型
根据上面四个参数,写出下面这段代码
- 最后一个参数空出来没有写,是因为这里涉及到一些重要的知识点
第四个参数是要写一个函数,能够比较base指向数组中的两个元素,并把结果返回。 那么就写一个函数叫cmp_int,参数就是cplusplus网站上qsort函数给的两个参数
代码如下:
int cmp_int(const void* p1, const void* p2)
- 写完了函数,怎么比较两个数呢?
在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); }
- 最后把结果打印出来
分装一个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排序各种类型的数据的内容,希望对您有帮助!