一、定義
什麼是回調函數?它首先是一個函數,這是肯定的,我舉個例子:有兩個函數A和B,首先應用程序將A的地址告訴B,這稱之爲註冊回調函數,B在收到外部某個命令的時候,可以根據B的地址來調用A,這個先被傳入B,後面又被B調用的函數A就稱之爲回調函數。
這樣看來,判斷一個函數是不是回調函數是從它被調用的方式來定義的,那麼B函數一般來說需要一個傳入參數,參數類型是函數指針。
二、應用場景
有人可能會說,B想調用A直接在函數裏面直接調用就好了,爲什麼先要把A的地址作爲一個參數傳給B這麼麻煩呢?
我們可以設想這樣一個生活場景:
商場裏有一款商品賣的特別好,經常賣斷貨,許多客戶都要求新貨一到就馬上聯繫他們,爲了商場能聯繫上客戶,客戶們就把自己的聯繫方式告訴商場(註冊回調函數),客戶A把自己的手機號告訴了商場,客戶B呢把自己的郵箱告訴了商場。等到新貨到了,商場就打電話(調用回調函數)通知客戶A,而通知客戶B則通過發郵件(調用回調函數)。
這裏我們可以看到,客戶把自己的聯繫方式告訴商場,至於是什麼聯繫方式商場並不關心,只要能聯繫上客戶就行,這樣就給客戶很大的靈活性。
再看一個具體的應用場景:
一個管理USB接口的模塊,這個模塊會不停的檢測USB接口是否有U盤插入。而當有U盤插入的這個事件發生時,不同的用戶可能想幹不同的事,比如有的用戶想在屏幕上顯示“有U盤插入了”,而有的用戶就想讓一個喇叭不停的響等等。那麼這個時候用戶只要把U盤插入響應函數的地址告訴模塊(註冊回調函數),等到模塊檢測到有U盤插入就去調用這個響應函數(調用回調函數),至於這個函數做些什麼,模塊並不關心,這就增加了用戶的靈活性,實現用戶想要的功能。
三、怎麼用
從上面我們看到需要把回調函數的地址作爲一個參數註冊到模塊中,這個函數地址其實就是個函數指針,既然要註冊到模塊中,這個回調函數必須以該模塊認可的格式編寫,說的比較抽象,下面舉個例子說明下。
先寫模塊部分,構造一個結構體
typedef struct _CheckEvent()
{
int eventID;
BOOL (*Check)(void);
}CheckEvent;
上面的結構體中定義了一個函數指針Check,參數是void,函數類型是BOOL,這就要求用戶定義的回調函數也必須是參數爲void、類型爲BOOL的函數。
void Module(void)
{
int index,id;
while(1)
{
id=GetEventID();
for(index=0;index<2;index++)
{
if(id == arrayCheckEvent[index]. eventID)//當事件eventID發生時
arrayCheckEvent[index]. Check();//調用eventID對應的回調函數
}
}
}
接着定義一個CheckEvent的數組:
CheckEvent arrayCheckEvent[2]=
{
{0, CheckEvent0},//註冊回調函數CheckEvent0
{1, CheckEvent1}//註冊回調函數CheckEvent1
};
編寫模塊主功能:
應用程序要做的就是實現回調函數
BOOL CheckEvent0(void)和BOOL CheckEvent1(void)了,實現用戶想要的功能,注意回調函數的參數和函數類型要和模塊中定義的CheckEvent結構體中的函數指針定義的一致。
作者: 心飛揚