C语⾔中的回调函数(CallbackFunction)
1 定义和使⽤场合
回调函数是指使⽤者⾃⼰定义⼀个函数,实现这个函数的程序内容,然后把这个函数(⼊⼝地址)作为参数传⼊别⼈(或系统)的函数中,由别⼈(或系统)的函数在运⾏时来调⽤的函数。函数是你实现的,但由别⼈(或系统)的函数在运⾏时通过参数传递的⽅式调⽤,这就是所谓的回调函数。简单来说,就是由别⼈的函数运⾏期间来回调你实现的函数。
这⼀设计允许了底层代码调⽤在⾼层定义的⼦程序(如图1-1所⽰)。C语⾔中回调函数主要通过函数指针的⽅式实现。
图1-1 回调函数在软件系统的调⽤结果
回调的⽤途⼗分⼴泛:[1]
例如,假设有⼀个函数,其功能为读取配置⽂件并由⽂件内容设置对应的选项。若这些选项由(hash function)所标记,则让这个函数接受⼀个回调会使得程序设计更加灵活:函数的调⽤者可以使⽤所希望的散列算法,该算法由⼀个将选项名转变为散列值的回调函数实现;因此,回调允许函数调⽤者在运⾏时调整原始函数的⾏为。
回调的另⼀种⽤途在于处理信号量。例如⼀个程序可能在收到信号时不愿⽴即终⽌;为了保证⼀切运⾏良好,该程序可以将清理函数注册为SIGTERM信号对应的回调。
回调亦可以⽤于控制⼀个函数是否作为:Xlib允许⾃定义的谓词(NSPredicate)⽤于决定程序是否希望处理特定的事件。
#include <iostream>
#include <string>
using namespace std;
typedef void (*FP)(char* s);    //结构体表⽰函数指针
void f1(char* s){cout<<s;}
void f2(char* s){cout<<s;}
void f3(char* s){cout<<s;}
int main(int argc,char* argv[])
{
int funcselector=0;          //定义⼀个整数⽤于控制待执⾏的函数
void* a[]={f1,f2,f3};  //定义了指针数组,这⾥a是⼀个普通指针
a[0]("Hello World!\n"); //编译错误,指针数组不能⽤下标的⽅式来调⽤函数
FP f[]={f1,f2,f3};      //定义⼀个函数指针的数组,这⾥的f是⼀个函数指针
/* Handle of funselector *///此处⽤于处理funselector,控制待执⾏的函数
f[funselector]("Hello World!\n"); //正确,函数指针的数组进⾏下标操作可以进⾏函数的间接调⽤
return0;
}
上⾯⼀个例⼦中提现了回调函数的部分作⽤。这⾥f1,f2,f3表⽰三个功能不相同的函数(举例说明:f1实现最⼤值输出,f2实现平均值输
出,f3实现最⼩值输出)。总结⼀下回调函数的⼀些优势:
采⽤funcselector作为标志量,选择待执⾏的函数很⽅便的控制了函数的流程和⼯序。
f1,f2,f3三个特定函数模块化明显,便于设计者去维护、修改。如图1-1所⽰,很多系统中software library会完全封装,这样开发者只能通过回调函数去修改函数功能。
分析函数思路更加清晰,在lwip中⼤量使⽤回调函数,开发者可以根据回调函数的调⽤流程分析系统结构。
2 结构解析
回调函数主要结构有三部分组成:主函数、调⽤函数和被调函数(如图1-1所⽰)。C语⾔中,被调函数通常以函数指针(指向对应函数的⼊
⼝地址)的形式出现。
这⾥给出⼀个最简单的回调函数结构,并解析相关数据结构。
//定义回调函数
void PrintfText()
{
printf("Hello World!\n");
}
//定义实现回调函数的"调⽤函数"
void CallPrintfText(void (*callfuct)())
{
callfuct();
}
//实现函数回调
int main(int argc,char* argv[])
{
CallPrintfText(PrintfText);
return0;
}
调⽤函数向其函数中传递void (*callfuct)(void)这是⼀个void callfuct(void)函数的⼊⼝地址,即PC指针可以通过移动到该地址执⾏void callfuct(void)函数,可以通过类⽐数组来理解。
实现函数调⽤中,函数调⽤了“调⽤函数”,再在其中进⼀步调⽤被“调⽤函数”。相⽐于主函数直接调⽤“被调函数”,这种⽅法为使⽤者,⽽不是开发者提供了灵活的接⼝。另外,函数⼊⼝可以像变量⼀样
设定同样为开发者提供了灵活性。
3 实例分析
这⾥分析⼀个lwip中较为复杂的回调函数使⽤范例:
void httpd_init(void)
{
struct tcp_pcb * pcb;
pcb = tcp_new();
tcp_bind(pcb,IP_ADDR_ANY,80);
pcb = tcp_listen(pcb);
tcp_accept(pcb, http_accept);
}
void
tcp_accept(struct tcp_pcb * pcb, err_t(* accept)(void *arg, struct tcp_pcb *newpcb, err_t err))
static err_t http_accept(void *arg, struct tcp_pcb * pcb, err_t err)
{
/* set the prio of callback function, important */
tcp_setprio(pcb, TCP_PRIO_MIN);
tcp_recv(pcb, http_recv);
return ERR_OK;
}
void
tcp_recv(struct tcp_pcb * pcb, err_t (* recv)(void * arg, struct tcp_pcb * tpcb, struct pbuf * p, err_t err))
recv函数
static err_t http_recv(void *arg, struct tcp_pcb * pcb, struct pbuf *p, err_t err)
{
/* html handler by user's definition */
/* use tcp_write(pcb, message, sizeof message, 0) to send message */
}
这⾥调⽤两个回调函数,模块化分离了tcp和http,感兴趣可以看看lwip的RAW部分。
4 参考资料
[1] /wiki/%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0
[2] partow/programming/templatecallback/index.html
[3] www.partow/programming/hashfunctions/index.html
[4] blog.csdn/callmeback/article/details/4242260
[5] wwwblogs/chenyuming507950417/archive/2012/01/02/2310114.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。