C语言回调函数
1.回調函數與普通函數的區別
從概念上講,回調函數與普通函數的本質在于:調用者的不同。普通函數由程序員代碼調用,而回調函數由操作系統在適當的時間調用。?回調函數主要用于處各種事件和處理。由于WINDOWS系統中存在大量程序員事先不可知的事件,例如鼠標的單擊,程序員事先無法得知終端用戶何時會發出此動作,因此只能:?
A。定義事件的處理邏輯,與普通函數的編程一樣;?
B。告之操作系統自己的處理邏輯,即通知操作系統函數指針;VC/VB等現代編程語言通過事件編程機制隱藏了這一步;?
C。操作系統在事件出現時,調用指定的函數(回調函數的概念)處理,這一步完全由系統負責。
??? 回調函數在各種操作系統中普遍存在,是現代操作系統為程序員提供處理異步事件的基本機制之一,在不同的系統中的具體實現方式各不相同;請參閱隨機文檔。Callback 函數實質就是你實現這個函數,由操作系統調用。而一般的情況下是,操作系統提供函數由你來調用的。
2.回調函數實際上就起到了消息循環的作用
??? 因為在sdk中只有通過回調函數來發送各自的處理消息.
3.C/C++實現
??? 象C/C++這樣支持函數指針的語言都有回調函數的概念,它實際上是向被調用函數傳一個你的函數地址,然后被調用函數向通過你傳入的函數地址來調用你的函數。比如你做了一個遍歷樹的函數,但你不知遍歷者將對各節點做何種處理時,你就可以在這個遍歷函數中加一個函數地址的參數,這樣調用者在遍歷該樹時就可以做各種有意義的工作了:比如打印各節點數據、匯總所有節點之類。
4.Windows回調函數
??? 回調函數是用來處理窗口消息的函數,一般類型為:
WindowProc(HWND hWnd,UINT message, WPARAM wParam, LPARAM lParam);
hWnd為窗口句柄,message為消息ID,后面兩個為消息參數。
??? MFC將一部分處理消息的函數封狀在CWnd類中,如OnCreate等,其參數也從WPARAM wParam, LPARAM lParam轉換為LPCREATESTRUCT結構(可以查看映射宏定義及MFC源代碼)而其他的有些也可以用回調函數,如WM_TIMER消息,可以在SetTimer函數里面第三個參數指定回調函數,若為NULL則應該在OnTimer函數中處理改消息。
5.MSDN中的描述
Used to asynchronously read the messages in a queue. It is an application-defined function that MSMQ calls when a message is available, a time-out occurs, or an error occurs.
6.Callback最本質的特征包括兩點:注冊和觸發
??? Callback函數是你提供給系統調用的函數。很多情況下,系統某個情況下,定義需要執行某個操作,而操作本身由有用戶的程序來提供,這時,就要用到回調函數了。所以,簡單地說。回調函數,就是你寫一個函數,在系統定義的地點提供給系統調用。
??? 舉個例子:SetTimer(),一種處理是,你響應WM_TIMER消息,這暫且不討論;還有一種用法,就是你提供一個函數,讓系統在產生timer消息時自動調用,這種情況下,你可以寫好一個timer消息的處理函數,把函數的地址作為SetTimer()的參數,而你這個timer消息的處理函數,就是回調函數。
??? 其實callback并不僅限于系統調用,用戶根據需要,可以建立自己的Callback機制。比如網絡通訊,當接收線程(可能專門有一個類封裝網絡接收行為)收到數據包,需要通知上層(可能又有一個類封裝上層數據處理).那么我認為Callback最本質的特征包括兩點:注冊和觸發。實現可以是各種各樣的形式,但機制都是如此。比如對于兩個類而言,給出以下示例代碼:
》》》建立自己的Callback機制
??? Callback函數有點類似虛函數,不僅僅系統調用,而且你自己也可以定義Callback函數,比如在自己的類中定義Callback函數的原型,然后在類的其他成員函數中就可以直接調用該Callback函數,而不用管他的具體實現,當然你可以傳入參數。而具體實現可能在其他應用程序中或者Dll中,這樣可以把接口和實現分離。
以前在某公司實習的時候還說過C語言的回調函數,現在在這說一下。
本代碼和語言參考 李先靜《系統程序員成長計劃》。
回調函數就是由內部實現統一接口,由調用者來決定調用哪一個函數,是對C語言函數指針的一個高級應用。比如我們在Linux內核里面,在設備驅動里面,例如有兩個不一樣的字符設備。那么對這個設備執行刷新操作(不管是否有刷新操作,這里只舉例)需要調用一定的函數,這兩個設備所執行的函數一定是不同的。但是,上層的管理數據結構是一樣的,在管理數據結構里面分別存入兩個刷新操作的函數指針,那么在調用的時候就可以直接用統一接口來執行刷新操作即可。這就是回調函數的一個例子,他實現了傳說中的Write Once,Run Anywhere.同時維護起來跟其他的相比更簡單。
那么就看看書上的例子:實現一個雙向鏈表(通用鏈表),它有遍歷鏈表、統計鏈表中最大值和鏈表值求和的功能。這三個功能可以寫成三個不同也不相關的函數,但是我們可以想到,這三個函數都有著相同的操作,那就是依次遍歷。那么就可以將這個遍歷抽出來(代碼中的dlist_foreach函數),然后根據這三個函數的各自的功能來實現較少重復的代碼。 具體代碼請看下面:
?Download?d_list.c| #include<stdio.h> #include<stdlib.h> #include<malloc.h>typedef struct _DListNode{struct _DListNode *prev;struct _DListNode *next;void *data; }DListNode;typedef void (*DListFunc)(void *ctx,void *data);void add_dlist_node_int(DListNode *root,DListNode *p){if(root==NULL)return;if(p==NULL)return;p->next=root->next;p->prev=root;root->next=p; }void delete_dlist_node(DListNode *d){if(NULL==d)return;print_int(NULL,d);d->prev->next=d->next;d->next->prev=d->prev;free(d->data);free(d);d=NULL; }void init(DListNode **root){int i=0;int *tmp=NULL;*root=(DListNode*)(malloc(sizeof(DListNode)));(*root)->data=(int *)(malloc(sizeof(int)));*((int *)(*root)->data)=-1;(*root)->next=(*root)->prev=*root;for(i=0;i<10;i++){DListNode *p=(DListNode *)(malloc(sizeof(DListNode)));tmp=(int *)(malloc(sizeof(int)));*tmp=i;p->data=tmp;add_dlist_node_int(*root,p);} }void dlist_foreach(DListNode *thiz,DListFunc visit,void *context){DListNode *iter=NULL;if(NULL==thiz)return;iter=thiz->next;while(iter!=NULL&&iter!=thiz){visit(context,iter);iter=iter->next;} }void print_int(void *thiz,DListNode *d){printf("======%d\n",*((int *)(d->data))); }void sum_cb(void *thiz,DListNode *d){(*(int *)(thiz))+=*((int *)(d->data)); }void find_max(void *thiz,DListNode *d){int a1=*((int *)(thiz));int a2=*((int *)(d->data));if(a1<a2){*((int *)thiz)=a2;} }void destroy(DListNode **root){DListNode *iter1=NULL,*iter2=NULL;if(NULL==(*root))return;iter1=*root;while(iter1!=NULL){iter2=iter1->next;if(iter2==iter1)iter2=NULL;delete_dlist_node(iter1);iter1=iter2;} }int main() {DListNode *root=NULL;int tmp=0;init(&root);if(root==NULL){printf("Root id NULL!\n");}dlist_foreach(root,print_int,&tmp);tmp=0;dlist_foreach(root,sum_cb,&tmp);printf("SUB IS: %d\n",tmp);tmp=0;dlist_foreach(root,find_max,&tmp);printf("MAX IS: %d\n",tmp);destroy(&root); } |
總結
- 上一篇: C++中实现回调机制的几种方式
- 下一篇: 系统程序员成长计划-组合的威力