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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM