深入了解回调函数

回调函数:顾名思义,是个函数,只不过这个函数是通过函数指针调用的函数,而不是由该函数的实现方直接调用的。如果把函数的指针(地址)作为参数传给另一个函数,当这个函数指针在另一个函数里面被调用时候,我们就称这个被调用的函数为回调函数,这种机制叫做回调函数机制。

这段话有一点绕,下面一张图可以搞清楚这个逻辑

 我们可以看到在主函数中我们实现了回调函数里面的功能,但却没有直接调用它,而是将它的地址给另一个函数,在另一个函数里面调用它,最终达到在主函数里面实现的效果。这样可以让回调函数在特定的场景下(即另一个函数实现了)才发挥作用。

下面我们来看这样一段代码:

#include<stdio.h>
int add(int x,int y)//加法函数
{
int x=0;
int y=0;
int z=x+y;
return z;}

int sub(int x,int y)//减法函数
{
int x=0;
int y=0;
int z=x-y;
return z;}

int mul(int x,int y)//乘法函数
{
int x=0;
int y=0;
int z=x*y;}

int div(int x,int y)//除法函数
{
int x=0;
int y=0;
int z=x/y;}

void menu()//菜单函数
{
printf("************************\n");
printf("****  1.add   2.sub*****\n");
printf("****  3.mul   4.div*****\n");
printf("************************\n");
}

void calc(int(*pf)(int,int))//调用回调函数(加减乘除)的函数
{
int x=0;
int y=0;
printf("请输入两个操作数");
scanf("%d%d",&x,&y);
printf("%d\n",pf(x,y));
}

int main()//主函数
{
int input=0;
do
{menu();
printf("请选择:");
scanf("%d",&input);
switch(input)
{
case 1:calc(add);//选择1的话将加法函数的地址传给calc函数,在calc函数里面进一步完成对加法函数的调用
break;
case 2:calc(sub);
break;
case 3:calc(mul);
break;
case 4:calc(div);
break;
case 0:printf"退出");
break;
default:printf("选择错误");
}}while(input);
return 0;

在这样一串代码中我们可以清楚的看到,利用一个calc函数,在calc函数里面将加减乘除函数地址传过去可以实现加减乘除,在主函数里面我们只需要直接调用calc函数即可。在这串代码中,calc()函数就是概念上的另一个函数,add().sub().mul().div()函数就是回调函数。

我们之前学过冒泡排序

#include<stdio.h>
void bubble_sort(int arr[],int sz)
{
int i=0;
int j=0;
for(i=0;i<sz-1;i++){
for(j=0;j<sz-1-i;j++)
{
if(arr[j]>arr[j+1]){
int tmp=0;
tmp=arr[j];
arr[i]=arr[j+1];
arr[j+1]=tmp;}}}}
int main()
{
int arr[10]={9,8,7,6,5,4,3,2,1,0};
int sz=sizeof(arr)/sizeof(arr[0]);
bubble_sort(arr,sz);
int i=0;
for(i=0;i<sz;i++)
printf("%d ",arr[i]);}
return 0;}

这种冒泡排序只能排序整型数组,如果数组换成浮点型,结构体类型就不能进行排序,那我们由什么方法可以实现一个通用的冒泡排序吗?我们这样想,浮点型,结构体型排序的思想也是冒泡排序,但是对于结构体而言,一些数据的比较大小肯定不能用大于小于来直接比较,所以这些类型和整型冒泡排序的区别就是,对于数据排序的方法是不同的。

在c中,由这样一个库函数qsort(),void qsort(void*base,size_t num,size_t width,int(*cmp)(const void *e1,const void *e2)。函数元素分别是目标数组首地址,目标数组元素个数,目标数组元素的字节大小,目标数组的比较方法。我们可以看到,qsort()函数用来接收数组首地址的形参是void*类型的,因为我们可能排序的数组类型是多样的,void*类型的指针可以用来接收任意类型的元素的地址;第四个参数cmp是一个函数指针,它用来指向的是比较方法函数的地址,这个比较方法函数需要我们自己根据要比较的数组类型来实现。(const void*e1,const void *e1)e1,e2是待比较的两个元素,如果e1比e2大则返回一个正值(e1将被排在e2后面),小则返回一个负值(e1将被排在e2前面),相等则返回0。

1.利用qsort()函数对整型数组排序:

#include<stdio.h>
int  cmp_int(const void*e1,const void*e2)
{
return *(int*)e1-*(int*)e2;}//将e1和e2强制类型转换为整型,这样才能对其进行解引用操作
//如果返回的数大于0,那么第一个元素将被排在第二个元素后面
//如果返回的数等于0,两个元素的位置不变
//如果返回的数小于0,第一个元素排在第二个元素前面
void test1()//qsort函数排序整型数组
{
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);
int i=0;
for(i=0;i<10;i++)
{
printf("%d ",arr[i]);}}
int main()
{test1();}

2.利用qsort()函数对浮点型数组排序:

#include<stdio.h>
int cmp_float(const*e1,const*e2)
{
return *(float*)e1-*(float*)e2;}

void test2()
{
float f[]={9.0,8.0,7.0,6.0,5.0,4.0,3.0,2.0,1.0};
int sz=sizeof(f)/sizeof(f[0]);
qsort(f,sz,sizeof(f[0]),cmp_float);
int j=0;
for(j=0;j<sz;j++)
{
printf("%d ",f[i]);}
return 0;}
int main()
{
test2();}

3.利用qsort函数排序结构体类型数组:

#include<stdio.h>
struct stu{char name;
int age;};

int cmp_stu_by_age(const*e1,const*e2)
{
return ((struct stu*)e1)->age-((struct stu*)e2)->age;
}

void test3()
{
struct stu s[3]={{"zhangsan",20},{"lisi",30},{"wangwu",10}};
int sz=sizeof(s)/sizeof(s[0]);
qsort(s,sz,sizeof(s[0]),int-cmp_stu_by_age);
}

int main()
{
test3();
return 0;}

上面三个代码我们已经学会了怎么使用qsort()这个库函数,那qsort()库函数里面又是怎么实现的呢?现在我们写一个函数,模拟qsot()库函数的功能。

#include<stdio.h>
void swap(char* buf1,char* buf2,int width){//交换待排序数组元素的每一个字节的函数
int i=0;
for(i=0;i<width;i++)//交换两个元素的每一个字节
{
char tmp=*buf1;
*buf1=*buf2;
*buf2=tmp;
buf1++;
buf2++;}}


void bubble_sort(void*base,int sz,int width,int (*cmp)(void* e1,void* e2)//通用冒泡排序函数---模拟实现qsort()函数的功能
{int i=0;
for(i=0;i<sz;i++){//比较的趟数
int j=0;
for(j=0;j<sz-1-i;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_int(const void*e1,const void*e2)//排序方法函数
{
return *(int*)e1- *(int*)e2;}//返回正数则将e1排在e2后面

void test4()//调用排序函数的函数
{
int arr[10]={9,8,7,6,5,4,3,2,1,0};
int sz=sizeof(arr)/sizeof(arr[0]);
bubble_sort(arr,sz,sizeof(arr[0]),cmp_int);
}

int main()
{test4();
return 0;}

整体设计代码思路:我们在调用test4函数来用冒泡排序函数可能会去排一个整型数组,也可能去排序一个结构体数组,那么在用这个冒泡排序函数的时候首先我们就要把数组名传过去,因为数组类型不一样,我们在用冒泡排序函数接收的时候的形参设计为void*类型,这样不管是什么类型的数组我们都可以接收;当然还需要把待排序数组的个数,和每个元素的字节宽度传过去,在用冒泡排序函数的时候,不同类型的数组的元素排序比较方法也不同,我们把这个比较两个元素的方法抽离出来做成一个函数,把这个函数的地址再传给冒泡排序函数。

在这个比较函数中,我们也不知道要比较的元素类型,所以将e1和e2设计成void*类型,我们要传的是两个元素的地址。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值