1、什么是回呼函式
說明:回呼函式其實不難!!!!
首先要明確的一點是,函式也可以作為函式的引數來傳遞,
所以可以理解為:把一段可執行的代碼像引數傳遞那樣傳給其他代碼,而這段代碼會在某個時刻被呼叫執行,這就叫做回呼,
如果代碼立即被執行就稱為同步回呼,如果過后再執行,則稱之為異步回呼,
回呼函式:就是一個通過函式指標呼叫的函式,如果你把函式的指標(地址)作為引數傳遞給另一個函式,當這個指標被用來呼叫其所指向的函式時,我們就說這是回呼函式,
回呼函式不是由該函式的實作方直接呼叫,而是在特定的事件或條件發生時由另外的一方呼叫的,用于對該事件或條件進行回應,
2 為什么要用回呼函式?
因為可以把呼叫者與被呼叫者分開,所以呼叫者不關心誰是被呼叫者,它只需知道存在一個具有特定原型和限制條件的被呼叫函式,
簡而言之,回呼函式就是允許用戶把需要呼叫的方法的指標作為引數傳遞給一個函式,以便該函式在處理相似事件的時候可以靈活的使用不同的方法,

int Callback() ///< 回呼函式
{
// TODO
return 0;
}
int main() ///< 主函式
{
// TODO
Library(Callback); ///< 庫函式通過函式指標進行回呼
// TODO
return 0;
}
回呼似乎只是函式間的呼叫,和普通函式呼叫沒啥區別,
但仔細看,可以發現兩者之間的一個關鍵的不同:
- 在回呼中,主程式把回呼函式像引數一樣傳入庫函式,
這樣一來,只要我們改變傳進庫函式的引數,就可以實作不同的功能,這樣有沒有覺得很靈活?并且當庫函式很復雜或者不可見的時候利用回呼函式就顯得十分優秀,
3 怎么使用回呼函式?
int Callback_1(int a) ///< 回呼函式1
{
printf("Hello, this is Callback_1: a = %d ", a);
return 0;
}
int Callback_2(int b) ///< 回呼函式2
{
printf("Hello, this is Callback_2: b = %d ", b);
return 0;
}
int Callback_3(int c) ///< 回呼函式3
{
printf("Hello, this is Callback_3: c = %d ", c);
return 0;
}
int Handle(int x, int (*Callback)(int)) ///< 注意這里用到的函式指標定義
{
Callback(x);
}
int main()
{
Handle(4, Callback_1);
Handle(5, Callback_2);
Handle(6, Callback_3);
return 0;
}
如上述代碼:可以看到,Handle()函式里面的引數是一個指標,在main()函式里呼叫Handle()函式的時候,給它傳入了函式Callback_1()/Callback_2()/Callback_3()的函式名,這時候的函式名就是對應函式的指標,也就是說,回呼函式其實就是函式指標的一種用法,
4 回呼函式實體(很有用)
一個GPRS模塊聯網的小專案,使用過的同學大概知道2G、4G、NB等模塊要想實作無線聯網功能都需要經歷模塊上電初始化、注冊網路、查詢網路資訊質量、連接服務器等步驟,這里的的例子就是,利用一個狀態機函式(根據不同狀態依次呼叫不同實作方法的函式),通過回呼函式的方式依次呼叫不同的函式,實作模塊聯網功能,如下:
/********* 作業狀態處理 *********/
typedef struct
{
uint8_t mStatus;
uint8_t (* Funtion)(void); //函式指標的形式
} M26_WorkStatus_TypeDef; //M26的作業狀態集合呼叫函式
/**********************************************
** >M26作業狀態集合函式
***********************************************/
M26_WorkStatus_TypeDef M26_WorkStatus_Tab[] =
{
{GPRS_NETWORK_CLOSE, M26_PWRKEY_Off }, //模塊關機
{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 }, //連接調度中心
{GPRS_NETWORK_WAIT_CTC, M26_WAIT_CTC }, //等待調度中心回復
{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模塊作業狀態機,依次呼叫里面的12個函式
***********************************************/
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模塊聯網專案,可以copy上面的框架,只需要修改回呼函式內部的具體實作,或者增加、減少回呼函式,就可以很簡潔快速的實作模塊聯網,
本文主要借鑒來自大牛的微信公眾號“嵌入式大雜燴”,公眾號上還有很多經典知識,講解通俗易懂,感謝作者,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/266298.html
標籤:其他
上一篇:烤面筋的第五場
下一篇:LC上一道有趣的并查集題目
