C语言——回调函数的典型示例(分析详解)
文章目录
- 一.什么是回调函数
- 二.qsort使用举例
- 2.1qsort函数的介绍
- 2.2qsort排序整型
- 2.3qsort排序结构数据
- 三.qsort函数的模拟实现
一.什么是回调函数
回调函数是通过函数指针调用的函数
把函数的地址(指针)
作为一个参数传递
给另一个函数,当这个指针被用来调用所指的函数的时候,这个被调用的函数
就是回调函数
回调函数不是
在由该函数的实现方调用的,而是在特定的事件条件下由另一方调用的,用于对该事件或条件进行响应。
实例
#include<stdio.h>
int Add(int x, int y)
{return x + y;
}
void test(int(*pf)(int ,int))
{int r = pf(10, 20);printf("%d\n", r);
}
int main()
{test(Add);return 0;
}
这时Add()就是回调函数,没有直接调用Add( )由该函数的实现方调用
(通过函数指针*pf调用Add()函数)。
直接调用
的方式:Add(10,20)
回调函数的应用
以指针章节的转移表为例:🚀C语言——深入解析C语言指针:从基础到实践从入门到精通(四)
#include<stdio.h>
int Add(int a, int b)
{return a + b;
}
int Sub(int a, int b)
{return a - b;
}
int Mul(int a, int b)
{return a * b;}
int Div(int a, int b)
{return a / b;
}
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 x, y;int input = 1;int ret = 0;do{Menu();printf("请选择:\n");scanf("%d", &input);switch (input){case 1:printf("请输入操作数:");scanf("%d %d", &x, &y);ret = Add(x, y);printf("%d\n", ret);break;case 2:printf("请输入操作数:");scanf("%d %d", &x, &y);ret = Sub(x, y);printf("%d\n", ret);break;case 3:printf("请输入操作数:");scanf("%d %d", &x, &y);ret = Mul(x, y);printf("%d\n", ret);case 4:printf("请输入操作数:");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;
}
会发现代码有许多冗长的部分,我们可以把冗长的代码用封装成一个函数Calc
,通过函数指针
作为函数Calc的参数,用来接收不同函数的地址,接受的地址不同,调用的函数就不同,就能实现相对应的功能
#include<stdio.h>
int Add(int a, int b)
{return a + b;
}
int Sub(int a, int b)
{return a - b;
}
int Mul(int a, int b)
{return a * b;}
int Div(int a, int b)
{return a / b;
}
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("%d\n", ret);
}
int main()
{int input = 1;do{Menu();printf("请选择:\n");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;
}
图解🤌:
接受的地址不同,调用的函数就不同,就能实现相对应的功能
二.qsort使用举例
在介绍qsort函数函数之前,之前讲解过冒泡排序,冒泡排序的方式是两个相邻元素逐个比较 ,但是冒泡排序只能进行整型间的排序。冒泡排序还是有一些不便利的地方
qsort是C语言提供的一个排序函数,是基于快速排序算法思想的一种排序方式
优点:
1.是现成的排序算法,可以直接使用
2.大部分情况下排序的效率比冒泡排序高
3.qsort函数可以排序任意类型的数据冒泡排序代码实现: 🚀C语言——深入解析C语言指针:从基础到实践从入门到精通(三)
2.1qsort函数的介绍
通过cplusplus网站查询qsort函数
qsort函数:
void qsort (void* base, size_t num, size_t size,int (compar)(const void,const void*));
参数介绍:
void* base:是指针,指向了被排序元素组的第一个元素
size_t num:base指向被排序数组的元素个数
size_t size:base指向被排序数组的元素大小(长度),单位是字节
int (compar)(const void,const void*)):函数指针,指针指向的函数是用来比较排序数组的两个元素。这个函数是根据排序的标准自己编写的一个比较函数这个比较函数的返回值是int类型的
当p1>p2时,返回>0
当p1=p2时,返回=0
当p1<p2时,返回<0
默认的排序是升序
官方网站上qsort函数的介绍
注:使用qsort要包含头文件
2.2qsort排序整型
以数组为例✍️
#include<stdio.h>
#include<stdlib.h>
void print_arr(int arr[], int sz)
{int i = 0;for ( i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");
}
int cmp_arr(const void* p1, const void* p2)
{if (*(int*)p1 > *(int*)p2){return 1;}else if (*(int*)p1 < *(int*)p2){return -1;}else{return 0;}
}
int main()
{int arr[10] = { 2,4,1,7,6,8,9,5,3,10 };int sz = sizeof(arr) / sizeof(arr[0]);print_arr(arr, sz);qsort(arr, sz, sizeof(arr[0]), cmp_arr);print_arr(arr, sz);return 0;
}
com_arr可以直接写成:
int cmp_arr(const void* p1, const void* p2)
{return (*(int*)p1 - *(int*)p2);}
如果想要进行降序
排列:
int cmp_arr(const void* p1, const void* p2)
{if (*(int*)p1 < *(int*)p2){return 1;}else if (*(int*)p1 > *(int*)p2){return -1;}else{return 0;}
}
return (*(int*)p2 - *(int*)p1);//等价
2.3qsort排序结构数据
结构体成员访问的方式:
struct stu
{char name[20];int age;
};
void test(struct stu *s)
{printf("%d\n", s->age);printf("%s\n", s->name);printf("%d\n", (*s).age);printf("%s\n", (*s).name);
}
int main()
{struct stu s = { "zhangsan",20 };test(&s);return 0;
}
用qsort函数排序结构体变量:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct stu
{char name[30];int age;
};void print_arr(struct stu s[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%s:%d ", s[i].name,s[i].age);}printf("\n");
}
int cmp_age(const void* p1, const void* p2)
{return (*(struct stu*)p1).age -(*(struct stu*)p2).age;
}
void test1()
{struct stu s[] = {{"zhangsan",19},{"wangli",20},{"xiaoming",18}};int sz = sizeof(s) / sizeof(s[0]);print_arr(s, sz);qsort(s, sz, sizeof(s[0]), cmp_age);print_arr(s, sz);
}
int cmp_name(const void* p1, const void* p2)
{return strcmp(((struct stu*)p1)->name , ((struct stu*)p2)->name);
}
void test2()
{struct stu s[] = { {"zhangsan",19},{"wangli",20},{"xiaoming",18} };int sz = sizeof(s) / sizeof(s[0]);print_arr(s, sz);qsort(s, sz, sizeof(s[0]), cmp_name);print_arr(s, sz);
}
int main()
{printf("按年龄打印\n");test1();printf("按名字打印\n");test2();return 0;
}
三.qsort函数的模拟实现
使用回到函数,模拟实现qsort函数(使用冒泡排序的方式)
#include<stdio.h>
int cmp_int(const void* p1, const void* p2)
{return (*(int*)p1 - *(int*)p2);
}
void Swap(char*buff1,char*buff2,size_t width)
{int i = 0;for ( i = 0; i < width; i++){int tmp = *buff1;*buff1 = *buff2;*buff2 = tmp;buff1++;buff2++;}
}
void print_arr(int arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");
}void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* p1, const void* p2))
{int i = 0;for ( i = 0; i < sz-1; i++){int j = 0;for ( j = 0; j < sz-i-1; j++){if (cmp((char*)base+j*width,(char*)base+(j+1)*width)>0){Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);}}}
}
void test()
{int arr[] = { 2,4,1,7,6,8,9,5,3,10 };int sz = sizeof(arr) / sizeof(arr[0]);print_arr(arr, sz);bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);print_arr(arr, sz);
}
int main()
{test();return 0;
}
任意类型的数据
struct stu{char name[20];int age;};
void Swap(char*buff1,char*buff2,size_t width)
{int i = 0;for ( i = 0; i < width; i++){int tmp = *buff1;*buff1 = *buff2;*buff2 = tmp;buff1++;buff2++;}
}void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* p1, const void* p2))
{int i = 0;for ( i = 0; i < sz-1; i++){int j = 0;for ( j = 0; j < sz-i-1; j++){if (cmp((char*)base+j*width,(char*)base+(j+1)*width)>0){Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);}}}
}
int cmp_age(const void* p1, const void* p2)
{return (*(struct stu*)p1).age -(*(struct stu*)p2).age;
}
int cmp_name(const void* p1, const void* p2)
{return strcmp(((struct stu*)p1)->name , ((struct stu*)p2)->name);
}
void print_arr2(struct stu s[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%s:%d ", s[i].name, s[i].age);}printf("\n");
}
void test2()
{struct stu s[] = { {"zhangsan",19},{"wangli",20},{"xiaoming",18} };int sz = sizeof(s) / sizeof(s[0]);printf("按名字排序\n");bubble_sort(s, sz, sizeof(s[0]), cmp_name);print_arr2(s, sz);printf("按年龄排序\n");bubble_sort(s, sz, sizeof(s[0]), cmp_age);print_arr2(s, sz);
}int main()
{test2();return 0;
}
swap函数实现原理
紫色框是编写者自己写的比较函数
棕色框是是实现库函数的人(厂商)规定的