文章目录
- 前言
- 一、什么是回调函数
- 二、qsort函数的介绍(默认升序排序)
- 三、qsort函数的模拟实现(通过冒泡排序)
- 总结
前言
本文介绍了回调函数,qsort函数的使用,以用冒泡排序来模拟实现qsort函数
提示:以下是本篇文章正文内容,下面案例可供参考
一、什么是回调函数
- 前面的博客里面我介绍了函数指针变量的相关概念,而回调函数就是通过一个函数指针调用的函数。进一步说,如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,被调用的函数就是回调函数,注意哈,回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生的时候由另一方调用哒,用于对该事件或条件进行响应。
- 我们使用回调函数其实可以简化代码,省去一些冗余重复的操作
以下是实现一个计算器的代码,我们在没有使用回调函数之前:
#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;}
int main(){int x, y;int input = 1;int ret = 0;do{//菜单:
printf("*************************\n");printf(" 1:add 2:sub\n");printf(" 3:mul 4:div\n");printf("*************************\n");printf("请选择:");scanf("%d", &input);switch (input){//这之后的代码就较为冗余,重复之处比较多case 1:printf("输⼊操作数:");scanf("%d %d", &x, &y);ret = add(x, y);printf("ret = %d\n", ret);break;case 2:printf("输⼊操作数:");scanf("%d %d", &x, &y);ret = sub(x, y);printf("ret = %d\n", ret);break;case 3:printf("输⼊操作数:");scanf("%d %d", &x, &y);ret = mul(x, y);printf("ret = %d\n", ret);break;case 4:printf("输⼊操作数:");scanf("%d %d", &x, &y);ret = div(x, y);printf("ret = %d\n", ret);break;//这里之前的代码较为冗余,重复地方过多case 0:printf("退出程序\n");break;default:printf("选择错误\n");break;}} while (input);return 0;}
在实现计算器的过程中,我们可以发现在case语句中输入输出较为冗余,重复次数过多,这里我们就可以用回调函数来简化代码,设置一个操作函数,参数为函数指针变量来简化代码。具体操作如下:
#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 calc(int(*pf)(int,int))
{
int ret = 0;
int x,y;
printf("输入操作数:");
scanf("%d %d",&x,&y);
ret = pf(x,y);//这里我们通过函数指针调用相关函数,所被调用的函数即为回调函数。
printf("ret=%d\n",ret);
}
int main(){int x, y;int input = 1;int ret = 0;do{//菜单:
printf("*************************\n");printf(" 1:add 2:sub\n");printf(" 3:mul 4:div\n");printf("*************************\n");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;}
很明显我重新定义了一个函数calc,而其参数为函数指针变量,我们将我们要执行的加减乘除函数传递到函数中,就可以减少重复的输入和输出代码,从而做到了简化代码的功效。
二、qsort函数的介绍(默认升序排序)
- qsort函数是我们C语言库中用来专门用来排序的库函数(头文件为:stdlib.h)
- 定义声明为:
- void qsort (void* base, size_t num, size_t size, int (compar)(const void,const void*));
- base代表待排序序列,num,为序列中元素个数,size,代表每个元素所代表的字节大小,最后一个参数为函数指针类型,是一个比较函数,用来阐述比较规则的。
- 对于最后一个参数函数指针类型的参数,我们通过它的返回值来确定具体那个元素在前,那个元素在后:
若返回值<0,则第一个指针指向的元素在前,第二个指针指向的元素在后;若返回值=0,则默认第一个指针指向的元素在前,第二个指向的元素在后;若返回值>0,则第一个指针指向的元素在后,第二个指针指向的元素在前。 - 使用qsort函数来排序整型数据:
代码显示:
#include<stdio.h>
#include<stdlib.h>
int int_cmp(const void *p1,const void*p2)
//实现泛式编程,我们定义void*指针,这样就可以接受任何类型的数据。后面只需要强制类型转换成我们所需要的数据类型即可。
{
return (*(int*)p1-*(int *)p2);
}
int main()
{
int arr[]={1,3,5,7,9,2,4,6,8,0};
int i =0;
qsort(arr,sizeof(arr)/sizeof(arr[0]),sizeof(int),int_cmp);//根据需要传递相应的参数。
for(int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
{
printf("%d ",arr[i] );
}
printf("\n");
}
- 使用qsort函数排序结构数据
- 我们来进行结构体的排序以学生结构体为例,我们进行分别以学生的名字为依据和学生的年龄为依据进行比较。
以年龄为依据进行排序:
#include<stdio.h>
#include<stdlib.h>
struct Stu //定义学生结构体变量
{
char name[20];//学生名字
int age;//年龄
};
//我们先以学生的年龄为依据进行比较
int cmp_stu_by_age(const void * e1,const void * e2)
{
return ((struct Stu*)e1)->age-((struct Stu*)e2)->age;
}
int main()
{
struct Stu arr[]={{"zhangsan,20"},{"lisi,30"},{"wangwu,35"}};//这里我们定义结构体序列
qsort(arr,sizeof(arr)/sizeof(arr[0]),sizeof(s[0]),cmp_stu_by_age);
}
return 0;
这里的排序结果我们通过调试来显示:
排序前:
排序后:
以名字为依据进行排序:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>//我们需要调用字符串函数strcmp函数进行字符串的比较
struct Stu //定义学生结构体变量
{
char name[20];//学生名字
int age;//年龄
};
int cmp_stu_by_name(const void*e1,const void * e2){
return strcmp( ((struct Stu*)e1)->name,((struct Stu*)e2)->name);
}int main()
{
struct Stu arr[]={{"zhangsan,20"},{"lisi,30"},{"wangwu,35"}};//这里我们定义结构体序列
qsort(arr,sizeof(arr)/sizeof(arr[0]),sizeof(s[0]),cmp_stu_by_age);
}
return 0;
我们通过调试来显示排序结果:
排序前:
排序后:
这里strcmp字符串比较函数的返回值正好符合我们qsort对于比较函数返回值的要求,二者可谓是不谋而合呀。
三、qsort函数的模拟实现(通过冒泡排序)
- 我在之前的博客里面已经实现过我们所熟悉的冒泡排序代码算法:
#include<stdio.h>
void input(int* arr, int sz)//输入待排序序列
{for (int i = 0; i < sz; i++){scanf("%d", arr + i);}
}void bubble_sort(int* arr, int sz)//冒泡排序算法
{for (int i = 0; i < sz-1; i++)//sz-1趟比较{int change = 1;//小优化节省时间for (int 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;change = 0;}}if (change == 1)//说明已经有序 {break;}}
}
void print(int* arr, int sz)
{for (int i = 0; i < sz; i++){printf("%d ", *(arr + i));}printf("\n");
}
int main()
{int arr[10] = { 0};int sz = sizeof(arr) /sizeof(arr[0]);input(arr, sz);bubble_sort(arr, sz);print(arr, sz);return 0;
}
但是在这里我们为了响应qsort算法,我们应该根据qort函数中的参数来重新改编冒泡排序。
前面已经提到过qsort函数的函数声明:
void qsort (void* base, size_t num, size_t size, int (compar)(const void,const void*));
这里我们用void指针来接受待排序序列,是一种泛式的编程,这里就可以接受任何数据类型的排序,这便是void指针的最大优势。
我们用冒泡排序模拟实现qsort函数的代码如下(这里我们以排序整型数据为例):
#include<stdio.h>
int int_cmp(const void*p1,const void * p2)//定义比较函数
{ return (*( int *)p1 - *(int *) p2);
}
void _swap(void*p1,void*p2,int size)
{
for(int i=0;i<size;i++)
{
//每一位字节都进行交换,从而做到整个数据类型进行交换。char tmp = *((char *)p1 + i);//我们转换成char*类型可以理解为转换成单位字节,然后乘上数据类型字节大小就可以表示任意数据类型*(( char *)p1 + i) = *((char *) p2 + i);*(( char *)p2 + i) = tmp;
}}
void bubble(void*base,int count ,int size,int(*cmp)(void*,void*))
//这里完全模仿qsort函数来定义的
{
for(int i=0;i<count-1;i++)
{
for(int j=0;j<count-1-i)
{
if(cmp((char*)base+j*size,(char*)base+(j+1)*size)>0)//我们转换成char*类型可以理解为转换成单位字节,然后乘上数据类型字节大小就可以表示任意数据类型
{_swap(( char *)base + j*size, (char *)base + (j + 1)*size, size);
}
}
}
}
int main(){int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };int i = 0;bubble(arr, sizeof(arr) /sizeof(arr[0]), sizeof (int), int_cmp);for (i = 0;i<sizeof(arr)/sizeof(arr[0]); i++){printf( "%d ", arr[i]);}printf("\n");return 0;}
总结
本文主要介绍了一个崭新的概念回调函数,并分析了qsort函数的使用,以及用冒泡排序来模拟qsort函数,如有错误,请批评指正,感谢支持