C 之回调函数

软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。同步调用是一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用;回调是一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口;异步调用是一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。回调和异步调用的关系非常紧密,通常我们使用回调来实现异步消息的注册,通过异步调用来实现消息的通知。同步调用是三者当中最简单的,而回调又常常是异步调用的基础

什么是回调函数?

  简而言之,回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

  关于函数指针,请参考:http://www.cnblogs.com/kunhu/p/3700610.html  

为什么要使用回调函数?

  因为可以把调用者与被调用者分开。调用者不关心谁是被调用者,所有它需知道的,只是存在一个具有某种特定原型、某些限制条件(如返回值为int)的被调用函数。

回调在C语言中是通过函数指针来实现的,通过将回调函数的地址传给被调函数从而实现回调。因此,要实现回调,必须首先定义函数指针:

1 void Func(char *s);// 函数原型
2 void (*pFunc) (char *);//函数指针

 



回调函数可以象普通函数一样被程序调用,但是只有它被当作参数传递给被调函数时才能称作回调函数。

被调函数的例子:

 1 void GetCallBack(pcb callback)
 2 {
 3 /*do something*/
 4 }
 5 用户在调用上面的函数时,需要自己实现一个pcb类型的回调函数:
 6 void fCallback(char *s) 
 7 {
 8 /* do something */
 9 } 
10 然后,就可以直接把fCallback当作一个变量传递给GetCallBack,
11 GetCallBack(fCallback);

 



如果赋了不同的值给该参数,那么调用者将调用不同地址的函数。赋值可以发生在运行时,这样使你能实现动态绑定。

回调函数是不能显式调用的函数;通过将回调函数的地址传给调用者从而实现调用。回调函数使用是必要的,在我们想通过一个统一接口实现不同的内容,这时用回掉函数非常合适。比如,我们为几个不同的设备分别写了不同的显示函数:void TVshow(); void ComputerShow(); void NoteBookShow()...等等。这是我们想用一个统一的显示函数,我们这时就可以用回掉函数了。

    void show(void (*ptr)());

使用时根据所传入的参数不同而调用不同的回调函数。

不同的编程语言可能有不同的语法,下面举一个c语言中回调函数的例子,其中一个回调函数不带参数,另一个回调函数带参数。

例子1:

 

//Test.c

 

 

 1 #include <stdlib.h>
 2 
 3 #include <stdio.h>
 4 
 5  
 6 
 7 int Test1()
 8 
 9 {
10 
11    int i;
12 
13    for (i=0; i<30; i++)
14 
15    {
16 
17      printf("The %d th charactor is: %c/n", i, (char)('a' + i%26));
18 
19     }
20 
21    return 0;
22 
23 }
24 
25 int Test2(int num)
26 
27 {
28 
29    int i;
30 
31    for (i=0; i<num; i++)
32 
33    {
34 
35     printf("The %d th charactor is: %c/n", i, (char)('a' + i%26));
36 
37     }
38 
39    return 0;
40 
41 }
42 
43  
44 
45 void Caller1(void (*ptr)())//指向函数的指针作函数参数
46 
47 {
48 
49    (*ptr)();
50 
51 }
52 
53 void Caller2(int n, int (*ptr)())//指向函数的指针作函数参数,这里第一个参数是为指向函数的指针服务的,
54 
55 { //不能写成void Caller2(int (*ptr)(int n)),这样的定义语法错误。
56 
57    (*ptr)(n);
58 
59    return;
60 
61 }
62 
63 int main()
64 
65 {
66 
67    printf("************************/n");
68 
69    Caller1(Test1); //相当于调用Test2();
70 
71    printf("&&&&&&************************/n");
72 
73    Caller2(30, Test2); //相当于调用Test2(30);
74 
75    return 0;
76 
77 }

 

 

 

 

以上通过将回调函数的地址传给调用者从而实现调用,但是需要注意的是带参回调函数的用法。要实现回调,必须首先定义函数指针。函数指针的定义这里稍微提一下。比如: int (*ptr)(); 这里ptr是一个函数指针,其中(*ptr)的括号不能省略,因为括号的优先级高于星号,那样就成了一个返回类型为整型的函数声明了。

 

C语言的标准库函数中很多地方就采用了回调函数来让用户定制处理过程。如常用的快速排序函数、二分搜索函数等。

快速排序函数原型:


1 void qsort(void *base, size_t nelem, size_t width, int (_USERENTRY *fcmp)(const void *, const void *));
3 二分搜索函数原型:
4 void *bsearch(const void *key, const void *base, size_t nelem,size_t width, int (_USERENTRY *fcmp)(const void *, const void *));

 

 

其中fcmp就是一个回调函数的变量。

下面给出一个具体的 C例子:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 int sort_function( const void *a, const void *b);
 4 int list[5] = { 54, 21, 11, 67, 22 };
 5 int main(void)
 6 {
 7    int  x;
 8    qsort((void *)list, 5, sizeof(list[0]), sort_function);
 9    for (x = 0; x < 5; x++)
10       printf("%i\n", list[x]);
11    return 0;
12 }
13 int sort_function( const void *a, const void *b)
14 {
15    return *(int*)a-*(int*)b;
16 }

 



再用c++ 写的一个简单的回调函数

   

 1 class CTest;
 2 typedef void (CTest::*DoMessageFunc)(char* msg, int msgid );
 3 class CTest
 4 {
 5 public:
 6     CTest(){}
 7     ~CTest(){}
 8     void DoMsgFunc1(char* pMsg,int nID)
 9     {
10         printf("%s\n",pMsg);
11         printf("回调函数\n");
12     }
13     void RegiestMsg(int nSrcID,DoMessageFunc pFunc)
14     {
15         m_pFunc = pFunc;
16     }
17     void HandleMessage(int nMsgID, char* pMsg, int nID)
18     {
19         (this->*m_pFunc)(pMsg,nID);
20     }
21 private:
22     DoMessageFunc m_pFunc;
23 };
24 using namespace std;
25 int main(int argc, char* argv[])
26 {
27     printf("Starting...... \n");
28     CTest obj ;
29     obj.RegiestMsg(12,&CTest::DoMsgFunc1);
30     obj.HandleMessage(1,"test",6);
31     printf("Ending...... \n");
32     return 0;
33 }

 


学习参考:
http://xenyinzen.wikidot.com/reship:080123-8
http://www.ibm.com/developerworks/cn/linux/l-callback/
http://blog.csdn.net/woyaowenzi/article/details/3950116
http://blog.chinaunix.net/uid-22488454-id-3057473.html
posted @ 2014-05-07 11:23  拦云  阅读(4365)  评论(0编辑  收藏  举报