C语言中的回调函数(Callback Function)
1 定义和使用场合
回调函数是指 使用者自己定义一个函数,实现这个函数的程序内容,然后把这个函数(入口地址)作为参数传入别人(或系统)的函数中,由别人(或系统)的函数在运行时来调用的函数。函数是你实现的,但由别人(或系统)的函数在运行时通过参数传递的方式调用,这就是所谓的回调函数。简单来说,就是由别人的函数运行期间来回调你实现的函数。
这一设计允许了底层代码调用在高层定义的子程序(如图1-1所示)。C语言中回调函数主要通过函数指针的方式实现。

图1-1 回调函数在软件系统的调用结果
回调的用途十分广泛:[1]
例如,假设有一个函数,其功能为读取配置文件并由文件内容设置对应的选项。若这些选项由散列值(hash function)所标记,则让这个函数接受一个回调会使得程序设计更加灵活:函数的调用者可以使用所希望的散列算法,该算法由一个将选项名转变为散列值的回调函数实现;因此,回调允许函数调用者在运行时调整原始函数的行为。
回调的另一种用途在于处理信号量。例如一个POSIX程序可能在收到SIGTERM信号时不愿立即终止;为了保证一切运行良好,该程序可以将清理函数注册为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=; //定义一个整数用于控制待执行的函数
void* a[]={f1,f2,f3}; //定义了指针数组,这里a是一个普通指针
a[]("Hello World!\n"); //编译错误,指针数组不能用下标的方式来调用函数 FP f[]={f1,f2,f3}; //定义一个函数指针的数组,这里的f是一个函数指针 /* Handle of funselector */ //此处用于处理funselector,控制待执行的函数
f[funselector]("Hello World!\n"); //正确,函数指针的数组进行下标操作可以进行函数的间接调用 return ;
}
上面一个例子中提现了回调函数的部分作用。这里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);
return ;
}
调用函数向其函数中传递 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,);
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)) 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] https://zh.wikipedia.org/wiki/%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0
[2] http://partow.net/programming/templatecallback/index.html
[3] http://www.partow.net/programming/hashfunctions/index.html
[4] http://blog.csdn.net/callmeback/article/details/4242260
[5] http://www.cnblogs.com/chenyuming507950417/archive/2012/01/02/2310114.html
C语言中的回调函数(Callback Function)的更多相关文章
- 理解javascript中的回调函数(callback)【转】
在JavaScrip中,function是内置的类对象,也就是说它是一种类型的对象,可以和其它String.Array.Number.Object类的对象一样用于内置对象的管理.因为function实 ...
- 理解javascript中的回调函数(callback)
以下内容来源于:http://www.jb51.net/article/54641.htm 最近在看 express,满眼看去,到处是以函数作为参数的回调函数的使用.如果这个概念理解不了,nodejs ...
- Scrapy - Request 中的回调函数callback不执行
回调函数callback不执行 大概率是被过滤了 两种方法: 在 allowed_domains 中加入目标url 在 scrapy.Request() 函数中将参数 dont_filter=True ...
- C语言中的回调函数
C语言中通过函数指针实现回调函数(Callback Function) ====== 首先使用typedef定义回调函数类型 ====== typedef void (*event_cb_t)(co ...
- javascript中的回调函数(callback)
代码如下: app.use(function(req, res, next) { var err = new Error('Not Found'); err.status = 404; ...
- 关于js中的回调函数callback
来源于:http://www.jianshu.com/p/6bc353e5f7a3 前言 其实我一直很困惑关于js 中的callback,困惑的原因是,学习中这块看的资料少,但是平时又经常见,偶尔复制 ...
- javascript中的回调函数(callback) (转载)
代码如下: app.use(function(req, res, next) { var err = new Error('Not Found'); err.status = 404; next(er ...
- 关于 js 中的回调函数 callback
本文写于1年前 曾经的学习文章如今拿出来分享 前言 其实我一直很困惑关于js中的callback,困惑的原因是,学习中这块看的资料少,但是平时又经常见,偶尔复制一下前人代码,功能实现了也就不再去追其原 ...
- 关于js中的回调函数callback,通俗易懂
前言 其实我一直很困惑关于js 中的callback,困惑的原因是,学习中这块看的资料少,但是平时又经常见,偶尔复制一下前人代码,功能实现了也就不再去追其原由,这么着,这个callback的概念就越来 ...
随机推荐
- Python基础篇【第5篇】: Python模块基础(一)
模块 简介 在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护. 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就 ...
- Tomcat服务相关
1. 将Tomcat安装成服务. 找到bin\service.bat文件,往cmd命令行窗口一拉,如果只提示service /remove [../..]那就是Tomcat和java的路径配置没问题. ...
- C++ 几种构造函数的区分和调用
class A { public: A(); //默认构造 A( A& a ); //copy构造 const A& operator=( const A& a ); //赋值 ...
- 修改.gitignore后让其生效
在使用git的时候我们有时候需要忽略一些文件或者文件夹.我们一般在仓库的根目录创建.gitignore文件在提交之前,修改.gitignore文件,添加需要忽略的文件.然后再做add commit p ...
- 批量删除wps文档里的回车符的方法!WPS使用技巧分享!
有时候整理文档的时候,如果是从网上复制的文字,可能会因为复制而产生很多的回车符.怎样能批量去掉这些个回车符呢,下面马上告诉你批量删除wps文档里的回车符的方法!WPS使用技巧分享! 想要批量删除批量删 ...
- OpenSSL漏洞补救办法详解(转)
CVE-2014-0160漏洞背景 2014年4月7日OpenSSL发布了安全公告,在OpenSSL1.0.1版本中存在严重漏洞(CVE-2014-0160).OpenSSL Heartbleed模块 ...
- CS 231n----Assignment1 记录
记录下在完成cs231n的Assignment1过程中的一些东西. 1. scores是一个N*C的array,N是训练样本个数,C是标签.y是(N,)的数组,取出每一个样本对应的score,可以用以 ...
- bcm cmd
BCM.1> port ge en=0 ;Disable all GbEBCM.1> tx 2 pbm=ge2,fe7 ;Transmit 2 packets out of both po ...
- 解决CentOS无法解析域名的问题
用SecureCRT连接到CentOS上,发现ping IP通,ping地址不同 [root@zyt-ceshi2 ~]# ping www.baidu.comping: unknown host w ...
- HTML。CSS浮动元素详解
浮动定位是指 1.1将元素排除在普通流之外,即元素将脱离标准文档流 1.2元素将不在页面占用空间 1.3将浮动元素放置在包含框的左边或者右边 1.4浮动元素依旧位于包含框之内 2. 浮动的框可以向左或 ...