C語言函數指針之回調函數

2020-12-17 C語言與CPP編程

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上面的框架,只需要修改回調函數內部的具體實現,或者增加、減少回調函數,就可以很簡潔快速的實現模塊聯網。

推薦閱讀

C/C++函數指針與指針函數

C語言指針詳解

相關焦點

  • 深入淺出剖析C語言函數指針與回調函數(一)
    今天我們要搞明白的一個概念叫回調函數。什麼是回調函數?
  • 深入淺出剖析C語言函數指針與回調函數
    回調函數就是一個通過函數指針調用的函數。如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用於對該事件或條件進行響應。
  • C/C++編程筆記:如何理解C語言中的回調函數,零基礎也看得懂
    在c語言中,回調是使用函數指針來實現的。函數指針——顧名思義,是指向一個函數的指針。通常函數指針有兩個方面的用途,一個是轉換表(jump table),另一個是作為參數傳遞給一個函數。下面是兩個函數指針的聲明:int(*f)(int,float);int*(*g[])(int,float);前者把f聲明為一個函數指針,它所指的函數接受兩個參數,分別是一個整型值和浮點型值,並返回一個整型值。
  • C語言的那些小秘密之函數指針
    我們經常會聽到這樣的說法,不懂得函數指針就不是真正的C語言高手。我們不管這句話對與否,但是它都從側面反應出了函數指針的重要性,所以我們還是有必要掌握對函數指針的使用。先來看看函數指針的定義吧。本文引用地址:http://www.eepw.com.cn/article/270442.htm  函數是由執行語句組成的指令序列或者代碼,這些代碼的有序集合根據其大小被分配到一定的內存空間中,這一片內存空間的起始地址就成為函數的地址,不同的函數有不同的函數地址,編譯器通過函數名來索引函數的入口地址,為了方便操作類型屬性相同的函數,c/c++引入了函數指針,函數指針就是指向代碼入口地址的指針
  • C++類與回調函數
    從C的回調函數說起在C語言中,回調函數是一個非常重要的概念
  • C語言陷阱與技巧25節,常說的「回調函數」是什麼?為何要用它?
    事實上,C語言函數指針的用途遠不止於此在本專欄更早的章節中,我們曾討論C語言函數的參數也可以是指針型的,「指針型」中的指針當然包括函數指針,也就是說,C語言函數的參數可以也是一個「函數」,只不過這個「函數」是通過函數指針傳遞的。
  • 快速上手系列-C語言之指針篇(四)函數與指針
    二維數組名作函數的參數:二維數組名作為參數,行可以不寫出來,列必須寫出來,如:數組指針作形參,完全等價於二維數組的應用指向函數的指針(函數指針):一個函數在編譯時被分配一個入口地址,這個地址就稱為函數的指針,函數名代表函數的入口地址。
  • C語言的指針,簡介
    指針,是一個表示變量或函數的地址的無符號整數。指針的字節數,與CPU的位數有關,32位機是4位元組,64位機是8位元組。與高級語言的long類型的大小是一致的。所以在java的JNI庫裡,如果需要把native層的C結構體的指針保存到java層,一般是定義一個long類型的變量。指針指向的變量,可以是普通變量、數組變量、結構體變量,以及數組元素、結構體的成員變量,還可以是指針變量。指針還可以指向函數,叫做函數指針。
  • C語言的那些小秘密之函數的調用關係
    在我們自己動手寫backtrace函數之前,先來看看glibc提供的backtrace函數的使用。:  int backtrace(void **buffer,int size)  該函數用來獲取當前線程的調用堆棧,獲取的信息將會被存放在buffer中,它是一個指針列表。
  • 指針在c語言中的妙用
    指針,任何一個計算機語言都少不了的好東西。特殊問題,特殊對待。函數指針就是這麼一個特殊的例子。比如:void Run(void){ PORTB = ~PORTB;}這個函數我們可以直接在main()裡調用它,也可以使用指針。
  • STM32Cube HAL庫中斷處理機制,回調函數實現原理
    像F0、F1...就是直接調用這個接收中斷函數來進一步處理。像L0、G0...是通過執行指針函數RxISR來進一步處理。2回調函數實現原理在HAL庫中存在大量類似HAL_XXX_XXXCallback這樣的函數,這些都是回調函數。回調函數就是一個通過函數指針調用的函數。如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。
  • C語言編程:以實例教你學指向函數的指針
    指針是C語言的精髓,對於初學者來講,指針是C語言語法學習中比較難的知識點,而這裡面指向函數的指針更是不太容易理解。下面給大家講下怎樣學習理解C語言中指向函數的指針及編程方法和使用例子。注意:這是一篇關於C語言編程的基礎語法內容,C語言大神請繞過。基本概念首先,先不要把指向函數的指針認為太難了,它和普通的指針區別也不是太大,只是定義形式上有所區別。
  • 小技巧:一種優雅的 C 語言函數指針的寫法
    上一篇文章中代碼很多,大家也許沒有注意到一個很巧妙的小技巧: 那就是 C 語言函數指針的寫法.在一次的代碼中的這樣的一行注釋 "很多同學不會寫函數指針聲明,函數指針的寫法是,先寫正常的函數聲明,然後將函數名加上括號,然後在函數名前再加上*號即可!!!
  • 深度剖析C語言的main函數
    在本文的最後,測試一下:  test.c:#include <stdio.h>int main(){    printf("c 語言");    return 11.1; }➜  testSigpipe git
  • C語言函數調用棧(二)
    4. thiscall調用約定C++類中的非靜態函數必須接收一個指向主調對象的類指針(this指針),並可能較頻繁的使用該指針。主調函數的對象地址必須由調用者提供,並在調用對象非靜態成員函數時將對象指針以參數形式傳遞給被調函數。編譯器默認使用thiscall調用約定以高效傳遞和存儲C++類的非靜態成員函數的this指針參數。
  • ...也可以面向對象面層,使用「函數指針結構體」為C語言找個「對象」
    上一節討論了C語言中的指針可以看作是一種普通的數據類型,這麼一來,函數指針數組就不難理解了,無非就是存放函數指針元素的數組而已。函數指針結構體稍微思考一下,應該能夠想到C語言中的普通數據類型不僅可以用於定義數組,還可以用來定義結構體,例如:struct cn{char c;int i;double d;};那麼可以看作「普通數據類型」的函數指針也可以定義結構體嗎?
  • C語言字符串處理函數之字符串轉換、查詢函數
    介紹完字符串整體操作函數,就該到字符串查詢函數和字符串轉換函數了,至於一些字符串轉換函數,如atoi(),atof(),strtod(),strtol(),tolower(),toupper()等,以後有時間再整理整理。
  • C語言編程技巧:如何在函數中正確返回字符串的指針
    問題提出在C語言編程中,我們經常會遇到這種情況,在某個函數中經過算法處理以後得到一個字符串類型的結果,可能需要將這個字符串以指針的形式進行返回,那麼如何在函數中正確返回該字符串的內容呢?例如,定義一個函數,要求該函數能夠返回一個指向字符串「I love C.」的指針並能在主程序中正確得到該字符串的內容。
  • NDK探究之旅《三》—C語言的輸入輸出函數
    NDK探究之旅《一》——對jni和NDK的認識NDK探究之旅《二》——C語言的基本認識有關於java的輸入輸出函數:C語言的輸出函數:我們先來看一下printf函數4,作用:在c語言中產生格式化輸出的函數(定義在 stdio.h 中),其向終端(顯示器、控制臺等)輸出字符。
  • 深度剖析C語言的main函數
    在本文的最後,測試一下:  test.c:#include <stdio.h>int main(){    printf("c 語言\n");    return 11.1; }在終端執行如下: