C語言函數(shù)指針之回調(diào)函數(shù)
1 什么是回調(diào)函數(shù)?
首先什么是“回調(diào)”呢?
我的理解是:把一段可執(zhí)行的代碼像參數(shù)傳遞那樣傳給其他代碼,而這段代碼會在某個時刻被調(diào)用執(zhí)行,這就叫做回調(diào)
。
如果代碼立即被執(zhí)行就稱為同步回調(diào)
,如果過后再執(zhí)行,則稱之為異步回調(diào)
。
回調(diào)函數(shù)
就是一個通過函數(shù)指針調(diào)用的函數(shù)。如果你把函數(shù)的指針(地址)作為參數(shù)傳遞給另一個函數(shù),當這個指針被用來調(diào)用其所指向的函數(shù)時,我們就說這是回調(diào)函數(shù)。
回調(diào)函數(shù)不是由該函數(shù)的實現(xiàn)方直接調(diào)用,而是在特定的事件或條件發(fā)生時由另外的一方調(diào)用的,用于對該事件或條件進行響應(yīng)。
2 為什么要用回調(diào)函數(shù)?
因為可以把調(diào)用者與被調(diào)用者分開,所以調(diào)用者不關(guān)心誰是被調(diào)用者。它只需知道存在一個具有特定原型和限制條件的被調(diào)用函數(shù)。
簡而言之,回調(diào)函數(shù)就是允許用戶把需要調(diào)用的方法的指針作為參數(shù)傳遞給一個函數(shù),以便該函數(shù)在處理相似事件的時候可以靈活的使用不同的方法。
int Callback() ///< 回調(diào)函數(shù)
{
// TODO
return 0;
}
int main() ///< 主函數(shù)
{
// TODO
Library(Callback); ///< 庫函數(shù)通過函數(shù)指針進行回調(diào)
// TODO
return 0;
}
回調(diào)似乎只是函數(shù)間的調(diào)用,和普通函數(shù)調(diào)用沒啥區(qū)別。
但仔細看,可以發(fā)現(xiàn)兩者之間的一個關(guān)鍵的不同:在回調(diào)中,主程序把回調(diào)函數(shù)像參數(shù)一樣傳入庫函數(shù)。
這樣一來,只要我們改變傳進庫函數(shù)的參數(shù),就可以實現(xiàn)不同的功能,這樣有沒有覺得很靈活?并且當庫函數(shù)很復雜或者不可見的時候利用回調(diào)函數(shù)就顯得十分優(yōu)秀。
3 怎么使用回調(diào)函數(shù)?
int Callback_1(int a) ///< 回調(diào)函數(shù)1
{
printf("Hello, this is Callback_1: a = %d ", a);
return 0;
}
int Callback_2(int b) ///< 回調(diào)函數(shù)2
{
printf("Hello, this is Callback_2: b = %d ", b);
return 0;
}
int Callback_3(int c) ///< 回調(diào)函數(shù)3
{
printf("Hello, this is Callback_3: c = %d ", c);
return 0;
}
int Handle(int x, int (*Callback)(int)) ///< 注意這里用到的函數(shù)指針定義
{
Callback(x);
}
int main()
{
Handle(4, Callback_1);
Handle(5, Callback_2);
Handle(6, Callback_3);
return 0;
}
如上述代碼:可以看到,Handle()
函數(shù)里面的參數(shù)是一個指針,在main()
函數(shù)里調(diào)用Handle()
函數(shù)的時候,給它傳入了函數(shù)Callback_1()/Callback_2()/Callback_3()
的函數(shù)名,這時候的函數(shù)名就是對應(yīng)函數(shù)的指針,也就是說,回調(diào)函數(shù)其實就是函數(shù)指針的一種用法。
4 回調(diào)函數(shù)實例(很有用)
一個GPRS
模塊聯(lián)網(wǎng)的小項目,使用過的同學大概知道2G、4G、NB
等模塊要想實現(xiàn)無線聯(lián)網(wǎng)功能都需要經(jīng)歷模塊上電初始化、注冊網(wǎng)絡(luò)、查詢網(wǎng)絡(luò)信息質(zhì)量、連接服務(wù)器等步驟,這里的的例子就是,利用一個狀態(tài)機函數(shù)(根據(jù)不同狀態(tài)依次調(diào)用不同實現(xiàn)方法的函數(shù)),通過回調(diào)函數(shù)的方式依次調(diào)用不同的函數(shù),實現(xiàn)模塊聯(lián)網(wǎng)功能,如下:
/********* 工作狀態(tài)處理 *********/
typedef struct
{
uint8_t mStatus;
uint8_t (* Funtion)(void); //函數(shù)指針的形式
} M26_WorkStatus_TypeDef; //M26的工作狀態(tài)集合調(diào)用函數(shù)
/**********************************************
** >M26工作狀態(tài)集合函數(shù)
***********************************************/
M26_WorkStatus_TypeDef M26_WorkStatus_Tab[] =
{
{GPRS_NETWORK_CLOSE, M26_PWRKEY_Off }, //模塊關(guān)機
{GPRS_NETWORK_OPEN, M26_PWRKEY_On }, //模塊開機
{GPRS_NETWORK_Start, M26_Work_Init }, //管腳初始化
{GPRS_NETWORK_CONF, M26_NET_Config }, /AT指令配置
{GPRS_NETWORK_LINK_CTC, M26_LINK_CTC }, //連接調(diào)度中心
{GPRS_NETWORK_WAIT_CTC, M26_WAIT_CTC }, //等待調(diào)度中心回復
{GPRS_NETWORK_LINK_FEM, M26_LINK_FEM }, //連接前置機
{GPRS_NETWORK_WAIT_FEM, M26_WAIT_FEM }, //等待前置機回復
{GPRS_NETWORK_COMM, M26_COMM }, //正常工作
{GPRS_NETWORK_WAIT_Sig, M26_WAIT_Sig }, //等待信號回復
{GPRS_NETWORK_GetSignal, M26_GetSignal }, //獲取信號值
{GPRS_NETWORK_RESTART, M26_RESET }, //模塊重啟
}/**********************************************
** >M26模塊工作狀態(tài)機,依次調(diào)用里面的12個函數(shù)
***********************************************/
uint8_t M26_WorkStatus_Call(uint8_t Start)
{
uint8_t i = 0;
for(i = 0; i < 12; i++)
{
if(Start == M26_WorkStatus_Tab[i].mStatus)
{
return M26_WorkStatus_Tab[i].Funtion();
}
}
return 0;
}
所以,如果有人想做個NB
模塊聯(lián)網(wǎng)項目,可以copy
上面的框架,只需要修改回調(diào)函數(shù)內(nèi)部的具體實現(xiàn),或者增加、減少回調(diào)函數(shù),就可以很簡潔快速的實現(xiàn)模塊聯(lián)網(wǎng)。
-END-
本文授權(quán)轉(zhuǎn)載自C語言與CPP編程,作者:LeeWay
推薦閱讀
免責聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!