C 之回調函數

軟件模塊之間老是存在着必定的接口,從調用方式上,能夠把他們分爲三類:同步調用、回調和異步調用。同步調用是一種阻塞式調用,調用方要等待對方執行完畢才返回,它是一種單向調用;回調是一種雙向調用模式,也就是說,被調用方在接口被調用時也會調用對方的接口;異步調用是一種相似消息或事件的機制,不過它的調用方向恰好相反,接口的服務在收到某種訊息或發生某種事件時,會主動通知客戶方(即調用客戶方的接口)。回調和異步調用的關係很是緊密,一般咱們使用回調來實現異步消息的註冊,經過異步調用來實現消息的通知。同步調用是三者當中最簡單的,而回調又經常是異步調用的基礎html

什麼是回調函數?linux

  簡而言之,回調函數就是一個經過函數指針調用的函數。若是你把函數的指針(地址)做爲參數傳遞給另外一個函數,當這個指針被用爲調用它所指向的函數時,咱們就說這是回調函數。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用於對該事件或條件進行響應。c++

  關於函數指針,請參考:http://www.cnblogs.com/kunhu/p/3700610.html  編程

爲何要使用回調函數?異步

  由於能夠把調用者與被調用者分開。調用者不關心誰是被調用者,全部它需知道的,只是存在一個具備某種特定原型、某些限制條件(如返回值爲int)的被調用函數。編程語言

回調在C語言中是經過函數指針來實現的,經過將回調函數的地址傳給被調函數從而實現回調。所以,要實現回調,必須首先定義函數指針:函數

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

 



回調函數能夠象普通函數同樣被程序調用,可是隻有它被看成參數傳遞給被調函數時才能稱做回調函數。學習

被調函數的例子:ui

 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()...等等。這是咱們想用一個統一的顯示函數,咱們這時就能夠用回掉函數了。this

    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
相關文章
相關標籤/搜索