C 語言回撥函式詳解
回撥函式就是一個透過函式指標呼叫的函式。如果你把函式的指標(地址)作為引數傳遞給另一個函式,當這個指標被用來呼叫其所指向的函式時,我們就說這是回撥函式。 |
回撥函式,光聽名字就比普通函式要高大上一些,那到底什麼是回撥函式呢?恕我讀得書少,沒有在那本書上看到關於回撥函式的定義。我在百度上搜了一下,發現眾說紛紜,有很大一部分都是使用類似這麼一個場景來說明:A君去B君店裡買東西,恰好缺貨,A君留下號碼給B君,有貨時通知A君。感覺這個讓人更容易想到的是非同步操作,而不是回撥。另外還有兩句英文讓我印象深刻:1) If you call me, I will call you back; 2) Don't call me, I will call you. 看起來好像很有道理,但是仔細一想,普通函式不也可以做到這兩點嗎?所以,我覺得這樣的說法都不是很妥當,因為我覺得這些說法都沒有把回撥函式的特點表達出來,也就是都看不到和普通函式到底有什麼差別。不過,百度百科的解析我覺得還算不錯(雖然經常吐槽百度搜尋...):回撥函式就是一個透過函式指標呼叫的函式。如果你把函式的指標(地址)作為引數傳遞給另一個函式,當這個指標被用來呼叫其所指向的函式時,我們就說這是回撥函式。
下面先說說我的看法。我們可以先在字面上先做個分解,對於"回撥函式",中文其實可以理解為這麼兩種意思:1) 被回撥的函式;2) 回頭執行呼叫動作的函式。那這個回頭呼叫又是什麼鬼?
先來看看來自維基百科的對回撥(Callback)的解析:In computer programming, a callback is any executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at a given time. This execution may be immediate as in a synchronous callback, or it might happen at a later time as in an asynchronous callback. 也就是說,把一段可執行的程式碼像引數傳遞那樣傳給其他程式碼,而這段程式碼會在某個時刻被呼叫執行,這就叫做回撥。如果程式碼立即被執行就稱為同步回撥,如果在之後晚點的某個時間再執行,則稱之為非同步回撥。關於同步和非同步,這裡不作討論,請查閱相關資料。
再來看看來自Stack Overflow某位大神簡潔明瞭的表述:A "callback" is any function that is called by another function which takes the first function as a parameter。 也就是說,函式 F1 呼叫函式 F2 的時候,函式 F1 透過引數給 函式 F2 傳遞了另外一個函式 F3 的指標,在函式 F2 執行的過程中,函式F2 呼叫了函式 F3,這個動作就叫做回撥(Callback),而先被當做指標傳入、後面又被回撥的函式 F3 就是回撥函式。到此應該明白回撥函式的定義了吧?
很多朋友可能會想,為什麼不像普通函式呼叫那樣,在回撥的地方直接寫函式的名字呢?這樣不也可以嗎?為什麼非得用回撥函式呢?有這個想法很好,因為在網上看到解析回撥函式的很多例子,其實完全可以用普通函式呼叫來實現的。要回答這個問題,我們先來了解一下回到函式的好處和作用,那就是解耦,對,就是這麼簡單的答案,就是因為這個特點,普通函式代替不了回撥函式。所以,在我眼裡,這才是回撥函式最大的特點。來看看維基百科上面我覺得畫得很好的一張圖片。
下面以一段不完整的 C 語言程式碼來呈現上圖的意思:
例項
#include#include// 包含Library Function所在讀得Software library庫的標頭檔案 int Callback() // Callback Function { // TODO return 0; } int main() // Main program { // TODO Library(Callback); // TODO return 0; }
乍一看,回撥似乎只是函式間的呼叫,和普通函式呼叫沒啥區別,但仔細一看,可以發現兩者之間的一個關鍵的不同:在回撥中,主程式把回撥函式像引數一樣傳入庫函式。這樣一來,只要我們改變傳進庫函式的引數,就可以實現不同的功能,這樣有沒有覺得很靈活?並且絲毫不需要修改庫函式的實現,這就是解耦。再仔細看看,主函式和回撥函式是在同一層的,而庫函式在另外一層,想一想,如果庫函式對我們不可見,我們修改不了庫函式的實現,也就是說不能透過修改庫函式讓庫函式呼叫普通函式那樣實現,那我們就只能透過傳入不同的回撥函式了,這也就是在日常工作中常見的情況。現在再把main()、Library()和Callback()函式套回前面 F1、F2和F3函式里面,是不是就更明白了?
明白了回撥函式的特點,是不是也可以大概知道它應該在什麼情況下使用了?沒錯,你可以在很多地方使用回撥函式來代替普通的函式呼叫,但是在我看來,如果需要降低耦合度的時候,更應該使用回撥函式。
知道了什麼是回撥函式,瞭解了回撥函式的特點,那麼應該怎麼使用回撥函式?下面來看一段簡單的可以執行的同步回撥函式程式碼。
例項
#includeint Callback_1() // Callback Function 1 { printf("Hello, this is Callback_1 "); return 0; } int Callback_2() // Callback Function 2 { printf("Hello, this is Callback_2 "); return 0; } int Callback_3() // Callback Function 3 { printf("Hello, this is Callback_3 "); return 0; } int Handle(int (*Callback)()) { printf("Entering Handle Function. "); Callback(); printf("Leaving Handle Function. "); } int main() { printf("Entering Main Function. "); Handle(Callback_1); Handle(Callback_2); Handle(Callback_3); printf("Leaving Main Function. "); return 0; }
執行結果:
Entering Main Function. Entering Handle Function. Hello, this is Callback_1 Leaving Handle Function. Entering Handle Function. Hello, this is Callback_2 Leaving Handle Function. Entering Handle Function. Hello, this is Callback_3 Leaving Handle Function. Leaving Main Function.
可以看到,Handle()函式里面的引數是一個指標,在main()函式里呼叫Handle()函式的時候,給它傳入了函式Callback_1()/Callback_2()/Callback_3()的函式名,這時候的函式名就是對應函式的指標,也就是說,回撥函式其實就是函式指標的一種用法。現在再讀一遍這句話:A "callback" is any function that is called by another function which takes the first function as a parameter,是不是就更明白了呢?
眼尖的朋友可能發現了,前面的例子裡面回撥函式是沒有引數的,那麼我們能不能回撥那些帶引數的函式呢?答案是肯定的。那麼怎麼呼叫呢?我們稍微修改一下上面的例子就可以了:
例項
#includeint Callback_1(int x) // Callback Function 1 { printf("Hello, this is Callback_1: x = %d ", x); return 0; } int Callback_2(int x) // Callback Function 2 { printf("Hello, this is Callback_2: x = %d ", x); return 0; } int Callback_3(int x) // Callback Function 3 { printf("Hello, this is Callback_3: x = %d ", x); return 0; } int Handle(int y, int (*Callback)(int)) { printf("Entering Handle Function. "); Callback(y); printf("Leaving Handle Function. "); } int main() { int a = 2; int b = 4; int c = 6; printf("Entering Main Function. "); Handle(a, Callback_1); Handle(b, Callback_2); Handle(c, Callback_3); printf("Leaving Main Function. "); return 0; }
執行結果:
Entering Main Function. Entering Handle Function. Hello, this is Callback_1: x = 2 Leaving Handle Function. Entering Handle Function. Hello, this is Callback_2: x = 4 Leaving Handle Function. Entering Handle Function. Hello, this is Callback_3: x = 6 Leaving Handle Function. Leaving Main Function.
可以看到,並不是直接把int Handle(int (*Callback)()) 改成 int Handle(int (*Callback)(int)) 就可以的,而是透過另外增加一個引數來儲存回撥函式的引數值,像這裡 int Handle(int y, int (*Callback)(int)) 的引數 y。同理,可以使用多個引數的回撥函式。
原文地址:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31559985/viewspace-2687928/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- C語言函式指標與回撥用函式C語言函式指標
- C語言函式指標與回撥函式使用方法C語言函式指標
- 函式回撥(C++)函式C++
- C++回撥函式 用法C++函式
- 函數語言程式設計 - 玩轉高階回撥函式函數程式設計函式
- C++中的回撥函式C++函式
- 回撥函式函式
- java回撥函式-非同步回撥-簡明講解Java函式非同步
- C語言解讀assert函式C語言函式
- 回撥函式(CallBack)函式
- JavaScript回撥函式JavaScript函式
- JavaScript 回撥函式JavaScript函式
- JS—回撥函式JS函式
- [JS]回撥函式和回撥地獄JS函式
- C++定義函式指標,回撥C#C++函式指標C#
- C語言回撥日誌庫的實現C語言
- 【不在混淆的C】指標函式、函式指標、回撥函式指標函式
- 函式指標&回撥函式Callback函式指標
- 回撥函式 與 函式閉包函式
- 回撥函式的作用函式
- Python/OpenCV:回撥函式PythonOpenCV函式
- TLS回撥函式(Note)TLS函式
- java 回撥函式示例Java函式
- C++_中介軟體kafka-回撥函式C++Kafka函式
- 在c中,怎麼註冊回撥函式函式
- C語言的函式C語言函式
- C語言 execve()函式C語言函式
- C語言常用函式C語言函式
- JS之回撥函式(callback)JS函式
- 回撥函式的理解(一)函式
- java回撥函式機制Java函式
- 【知識點】inline函式、回撥函式、普通函式inline函式
- C語言 函式指標C語言函式指標
- C語言基礎函式C語言函式
- 08. C語言函式C語言函式
- C語言函式呼叫棧C語言函式
- C語言函式傳遞指標引數的問題詳解C語言函式指標
- C語言#define詳解C語言