更新:
随着理解的深入和在实际项目中的应用,才发现回调函数真正使用的场合和好处。在实际的使用场景中经常会出现这样的情况,两拨人共同开发一个项目,但互相又不想让对方看到自己的源码,在功能上又会互相有函数调用关系,他调用用你的函数,你调用他的函数。假如对方是一个库(动态库或者静态库),集成在你这边的main函数中使用,对方要调用你的函数就要使用回调函数,你调用对方的函数可以调用用对方(库)封装的函数。
main函数:mian.c文件
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <lib.h>
//回调函数 功能函数
void addfun()
{
printf("this is a test\n");
}
int main(int argc,char **argv)
{
//注册回调
mytest =addfun;
//调用库中的函数
addtest();
return 0;
}
lib.h
#ifndef __LIB_H__
#define __LIB_H__
//定义一个函数指针
typedef void (*func)(void);
//定义一个函数指针对象
func mytest;
//库封装函数
void addtest(void);
#endif
lib.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <lib.h>
//库封装函数
void addtest()
{
printf("callback test\n");
}
//回调宏函数测试
void test()
{
//调用
mytest();
}
在很长一段时间里,我都没理解回调函数在实际应用中的意义和好处,因为在网上查了很多博主的相关博客,讲的都大同小异,没有根据实际应用去举例子,只是介绍了一下回调函数的使用和概念,那些介绍都让我没办法理解回调函数。后来在和一个做软件的同事沟通后,豁然开朗,有了一些理解,在此将这些理解记录下来。
回调函数的目的就是为了解耦(理论上的意义),实际上回调函数的作用有两点:
1 底层调用上层的方法去实现其他组合的逻辑(比如利用上层提供的打快门方法去实现做fnd(这里的fnd可以理解为一个包含了打快门的复杂的方法),打快门的实际操作函数是上层提供的,底层并不关心上层是如何实现的,只需要定义好格式调用即可,这也方便了解耦)。
2 底层返回一些必要的数据给上层使用(比如usb摄像头在usb捕获到摄像头的数据回传给上层应用去显示等),这样,上层不用等底层的数据,有就接收数据,没有就做别的事情。
这样看来,回调函数的应用是双向的,并不局限于上层向底层传方法。
我们把上层实现的那个函数(打快门的函数)叫做回调函数。
//20211210 testtcode
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
//定义函数指针结构体
typedef struct somefun{
int (*pfun)(int ,int);
}myperson;
//定义回调函数
int myfun(int a,int b )
{
return a+b;
}
//中间函数
int callback(int a ,int b,int (*pfun)())
{
//调用回调函数,这里的调用只是简单举例,实际并不是这样简单,可能在底层会有一次写复杂的逻辑在调用这个回调函数或者特定的条件才会触发,这个是理解回调的一个很重要的知识点,因为如果是这种简单的调用的话,完全没有必要使用回调,也无法体现回调的优势。
return ((*pfun)(a,b));
}
void addfun()
{
printf("this is a test\n");
}
int main(int argc,char **argv)
{
char *P ="my_world";
char b[]={"soartui"};
b[2]='F';
printf("b = %s\n",b);
int result=0;
myperson person1;
person1.pfun=myfun;
//注册回调函数
result = callback(2,3,person1.pfun);
printf("result = %d\n",result);
printf("addfun addr = %p\n",addfun );
((void (*)(void))0x4005a3)();
printf("helloworld!\n");
const int aa=0x11223344;
char *p1 =&aa;
p1[1]=0x55;
printf("aa is %x\n",aa);
return 0;
}
上面这个例子还不足以说明回调函数在上下层的使用,所以再举一个例子去说明回调函数上下层传数据的意义和妙处。假如call_back是一个库,那他既可以调用上层的函数getsome_value()去做库里面的操作,又可以返回数据给上层去使用,这样的方式理解是不是就很容易了。
#include <stdio.h>
#include <stdlib.h>
//回调函数,生成一个随机数
int getsome_value(int a)
{
if(a>5)
{
a=rand();
return a;
}
else
return 0;
}
//注册回调函数,这里面一个是调用回调函数,一个是返回调用函数后生成的数据,这个才是回调函数在
//实际应用中会用到的情况,加入这个callback是库中的函数,那么库中可能会需要调用getsome_value()这个方法, 也有可能会返回一些数据,比如数据流,视频流等。
void call_back(int *array, int size,int (*getnext)(int),int value)
{
for(int i =0;i<size;i++)
{
array[i]= getnext(value);//调用回调函数并赋值
}
}
int main(int argv, char **argc)
{
int arr[10];
call_back(arr,10,getsome_value,7);//注册回调函数
for(int i=0;i<10;i++)
{
printf("%d\r\n",arr[i]);
}
call_back(arr,10,getsome_value,3);
for(int i=0;i<10;i++)
{
printf("%d\r\n",arr[i]);
}
return 0;
}