[技术问答] C语言中的回调函数

[复制链接]
 楼主| 51xlf 发表于 2023-3-14 20:20 | 显示全部楼层 |阅读模式
1 定义和使用场合
回调函数是指 使用者自己定义一个函数,实现这个函数的程序内容,然后把这个函数(入口地址)作为参数传入别人(或系统)的函数中,由别人(或系统)的函数在运行时来调用的函数。函数是你实现的,但由别人(或系统)的函数在运行时通过参数传递的方式调用,这就是所谓的回调函数。简单来说,就是由别人的函数运行期间来回调你实现的函数。
这一设计允许了底层代码调用在高层定义的子程序(如图1-1所示)。C语言中回调函数主要通过函数指针的方式实现。

图1-1 回调函数在软件系统的调用结果

回调的用途十分广泛:[1]
例如,假设有一个函数,其功能为读取配置文件并由文件内容设置对应的选项。若这些选项由散列值(hash function)所标记,则让这个函数接受一个回调会使得程序设计更加灵活:函数的调用者可以使用所希望的散列算法,该算法由一个将选项名转变为散列值的回调函数实现;因此,回调允许函数调用者在运行时调整原始函数的行为。
回调的另一种用途在于处理信号量。例如一个POSIX程序可能在收到SIGTERM信号时不愿立即终止;为了保证一切运行良好,该程序可以将清理函数注册为SIGTERM信号对应的回调。
回调亦可以用于控制一个函数是否作为:Xlib允许自定义的谓词(NSPredicate)用于决定程序是否希望处理特定的事件。
  1. #include <iostream>
  2. #include <string>
  3. using namespace std;

  4. typedef void (*FP)(char* s);    //结构体表示函数指针
  5. void f1(char* s){cout<<s;}
  6. void f2(char* s){cout<<s;}
  7. void f3(char* s){cout<<s;}

  8. int main(int argc,char* argv[])
  9. {
  10.     int funcselector=0;           //定义一个整数用于控制待执行的函数
  11.     void* a[]={f1,f2,f3};   //定义了指针数组,这里a是一个普通指针
  12.     a[0]("Hello World!\n"); //编译错误,指针数组不能用下标的方式来调用函数

  13.     FP f[]={f1,f2,f3};      //定义一个函数指针的数组,这里的f是一个函数指针
  14.    
  15.     /* Handle of funselector */       //此处用于处理funselector,控制待执行的函数
  16.     f[funselector]("Hello World!\n"); //正确,函数指针的数组进行下标操作可以进行函数的间接调用
  17.    
  18.     return 0;
  19. }     


上面一个例子中**了回调函数的部分作用。这里f1,f2,f3表示三个功能不相同的函数(举例说明:f1实现最大值输出,f2实现平均值输出,f3实现最小值输出)。总结一下回调函数的一些优势:
采用funcselector作为标志量,选择待执行的函数很方便的控制了函数的流程和工序。
f1,f2,f3三个特定函数模块化明显,便于设计者去维护、修改。如图1-1所示,很多系统中software library会完全封装,这样开发者只能通过回调函数去修改函数功能。
分析函数思路更加清晰,在lwip中大量使用回调函数,开发者可以根据回调函数的调用流程分析系统结构。

2 结构解析
回调函数主要结构有三部分组成:主函数、调用函数和被调函数(如图1-1所示)。C语言中,被调函数通常以函数指针(指向对应函数的入口地址)的形式出现。
这里给出一个最简单的回调函数结构,并解析相关数据结构。
  1. //定义回调函数
  2. void PrintfText()
  3. {
  4.     printf("Hello World!\n");
  5. }

  6. //定义实现回调函数的"调用函数"
  7. void CallPrintfText(void (*callfuct)())
  8. {
  9.     callfuct();
  10. }

  11. //实现函数回调
  12. int main(int argc,char* argv[])
  13. {
  14.     CallPrintfText(PrintfText);
  15.     return 0;
  16. }



调用函数向其函数中传递 void (*callfuct)(void) 这是一个 void callfuct(void) 函数的入口地址,即PC指针可以通过移动到该地址执行void callfuct(void) 函数,可以通过类比数组来理解。
实现函数调用中,函数调用了“调用函数”,再在其中进一步调用被“调用函数”。相比于主函数直接调用“被调函数”,这种方法为使用者,而不是开发者提供了灵活的接口。另外,函数入口可以像变量一样设定同样为开发者提供了灵活性。

3 实例分析
这里分析一个lwip中较为复杂的回调函数使用范例:


  1. void httpd_init(void)
  2. {
  3.     struct tcp_pcb * pcb;
  4.     pcb = tcp_new();
  5.     tcp_bind(pcb,IP_ADDR_ANY,80);
  6.     pcb = tcp_listen(pcb);
  7.     tcp_accept(pcb, http_accept);        
  8. }

  9. void
  10. tcp_accept(struct tcp_pcb * pcb, err_t(* accept)(void *arg, struct tcp_pcb *newpcb, err_t err))

  11. static err_t http_accept(void *arg, struct tcp_pcb * pcb, err_t err)
  12. {
  13.     /* set the prio of callback function, important */
  14.     tcp_setprio(pcb, TCP_PRIO_MIN);
  15.     tcp_recv(pcb, http_recv);
  16.     return ERR_OK;   
  17. }   

  18. void
  19. tcp_recv(struct tcp_pcb * pcb, err_t (* recv)(void * arg, struct tcp_pcb * tpcb, struct pbuf * p, err_t err))

  20. static err_t http_recv(void *arg, struct tcp_pcb * pcb, struct pbuf *p, err_t err)
  21. {
  22.     /* html handler by user's definition */
  23.     /* use tcp_write(pcb, message, sizeof message, 0) to send message */
  24. }




tpgf 发表于 2023-4-7 14:20 | 显示全部楼层
回调函数和指针这两者是密不可分的
qcliu 发表于 2023-4-7 14:47 | 显示全部楼层
回调函数就是一个通过函数指针调用的函数
drer 发表于 2023-4-7 15:05 | 显示全部楼层
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
coshi 发表于 2023-4-7 15:35 | 显示全部楼层
在回调中,主程序把回调函数像参数一样传入库函数
kxsi 发表于 2023-4-7 16:03 | 显示全部楼层
当库函数很复杂或者不可见的时候利用回调函数就显得十分优秀
wiba 发表于 2023-4-7 16:31 | 显示全部楼层
回调函数其实就是函数指针的一种用法
LOVEEVER 发表于 2023-4-11 14:35 | 显示全部楼层

回调函数就是一个通过函数指针调用的函数
youtome 发表于 2023-4-15 19:42 | 显示全部楼层
对指针的应用是C语言编程的精髓所在,而回调函数就是C语言里面对函数指针的高级应用。简而言之,回调函数是一个通过函数指针调用的函数。
burgessmaggie 发表于 2023-4-15 19:48 | 显示全部楼层
c语言回调函数有什么用处?              
wengh2016 发表于 2023-4-15 19:53 | 显示全部楼层
callback函数怎么用?   
robertesth 发表于 2023-4-15 19:59 | 显示全部楼层
回调函数 就是上层调用 设置下去 底层通过函数指针调用上层函数
1988020566 发表于 2023-4-15 20:06 | 显示全部楼层
等该函数执行完了,会回去调用我们传进去的函数。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

547

主题

9919

帖子

24

粉丝
快速回复 在线客服 返回列表 返回顶部

547

主题

9919

帖子

24

粉丝
快速回复 在线客服 返回列表 返回顶部