C語言、嵌入式重點知識:回調函數

前言

上文分享了一個專用的雙鏈表的基本操作示例:

這裡提到了一個關鍵詞:專用。與專用對應的詞是通用。我們從字面上可以很容易理解這兩個詞,專用就是針對特定情況的,特點就是很有侷限性;通用就是可以針對大多數情況(更理想的就是所有情況),特點就是適用性廣。

為什麼說上篇筆記的雙鏈表是專用的?

C語言、嵌入式重點知識:回調函數

從我們的定義的元素數據類型就可以知道,我們這個雙鏈表是隻是用來存儲int類型的數據的,這就很能體現出了局限性(這只是其中一點,當然還有其它的很多侷限性),因此是個專用的雙鏈表。

我們要編寫一個通用的雙鏈表的話,我們首先要做的是就是修改雙鏈表結點結構體了,可以修改為:

C語言、嵌入式重點知識:回調函數

如果我們要存放整數,我們可以把void*強制轉換成整數使用。當然這篇筆記的重點不是分享通用的雙鏈表。我們這篇筆記要分享的是回調函數,下面進入重點內容:

回調函數法 VS 常規法

我們上篇筆記中有一個打印輸出鏈表數據的函數:

C語言、嵌入式重點知識:回調函數

這是我們這個專用的雙鏈表中打印鏈表數據函數,我們存儲的是整數,所以用%d打印。那麼,如果我們面向的是通用的雙鏈表呢?我們無法預知其中的數據,可能是整數,也可能是字符串,或者是其它的數據。那麼怎麼辦呢?這裡有幾種方法:

方法一:實現多個函數,需要用到哪個就調哪個

C語言、嵌入式重點知識:回調函數

比如存放的是整數,可以調用dlist_print_int函數來打印;存放的是字符串,可以調用dlist_print_string函數來打印。

這種方法很簡單,但有個缺點:每個函數都很相似,會有大量重複的代碼。


方法二:傳入一個附加的參數來選擇打印的方式

C語言、嵌入式重點知識:回調函數

這種方法使用一個參數來選擇打印的方式。避免了方法一中產生大量重複的代碼的問題。但是我們每當要增加新類型時,都得修改這個dlist_print函數,對於一個通用的雙鏈表來說,這樣的修改是不夠好的。這裡dlist_print函數也是通用雙鏈表的一部分,我們應該儘量少去修改它。

假如我們把一個通用的雙鏈表的基礎操作比喻做一棟樓房的地基,地基一旦牢牢固固的搭好之後,我們就不要再去動它了, 應該把精力放在如何搭建房子的上層上。


方法三:回調函數法

上面兩種方法應該是很容易想到的方法。現在來分享我們可能想不到的方法——回調函數法,這也是本篇筆記要分享的重點。

可能有很多朋友沒用過回調函數,甚至有些朋友都沒聽說過。這裡先簡單介紹回調函數的一些概念(以下概念來自百度百科):

回調函數就是一個通過函數指針調用的函數。如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。

回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用於對該事件或條件進行響應。

知識點:變量指針指向的是一塊數據,指針指向不同的變量,則取到的是不同的數據;函數指針指向的是一段代碼(即函數),指針指向不同的函數,則具有不同的行為。

迴歸正題,下面看如何使用回調函數法來實現上面的需求。

C語言、嵌入式重點知識:回調函數

首先,我們需要實現一個通用的打印函數dlist_print,把函數指針變量作為其中一個參數傳入。其次,我們調用者得根據實際情況實現一個用於打印的回調函數,這裡我們實現的的回調函數是dlist_print_int。最後,在用到打印的地方調用dlist_print函數即可。

用回調函數法是不是很巧妙?

此處,我們用到了typedef來“封裝”一個打印鏈表數據的函數指針類型,這可能會刷新了初學者對於typedef關鍵字的認識。因為我們剛開始學C語言的時候,總認為typedef取別名的一般形式為:

<code>typedef 舊名字 新名字;/<code>

確實也是這樣,但遇到給函數指針類型、數組類型等定義別名的時候就要特別區分了。如:

<code>typedef char ARRAY20[20];
ARRAY20 a1,a2; /* 等價於char a1[20],a2[20]; *//<code>

別問我為什麼,就是這樣的。。。


回調函數的例子

上面分析了那麼多,可能很多朋友會覺得回調函數太麻煩了,沒必要用。但是現實是,回調函數在我們的C編程、嵌入式編程中用得很廣泛。

1、在C編程中

在C語言的通用工具庫stdlib.h中,有如下一個函數原型:

<code>void qsort(void *, size_t, size_t, int (comp*)(const void *, const void *));/<code>

這是在C通用工具庫中聲明的一個快速排序算法函數,其可以用來排序int類型、float類型以及字符串數據,可以按從小到大的順序也可以按從大到小的順序排序。其關鍵在於函數指針comp指向的函數的具體實現。


2、在嵌入式編程中

我們之前的筆記:【RT-Thread筆記】PIN設備中斷配置中,就有用到回調函數。RT-Thread給我們提供了PIN設備中斷回調綁定函數:rt_pin_attach_irq

C語言、嵌入式重點知識:回調函數

C語言、嵌入式重點知識:回調函數

這是個中斷實驗,產生中斷會回調我們的回調函數,所以可以在在我們的回調函數里做一些產生中斷後需要做的操作。比如我們在這個中斷回調裡打印一串字符串。每當中斷來時,就會打印該字符串:

C語言、嵌入式重點知識:回調函數


總結

回調函數是一個很重要的知識點,我們需要掌握。而回調函數又與函數指針聯繫密切,我們要努力把函數指針弄懂、用熟。在C語言中,指針很重要,函數指針更重要。正如前輩們常說類似這樣子的話:不會C指針,就沒學會C語言;不會函數指針,就不要稱自己是C語言高手。

在這幾種方法中的分析中,其實回調函數更多的是體現出了軟件分層的思想。分層思想在我們軟件開發中是一種很重要的思想,簡單的分層我們都會,但是怎麼才能算是分層分得很好呢?那就是不該動的地方不動,該動的地方才動,銜接得很好,就像上面的回調函數法。

對於編程的學習,關於編程語言的學習,知識點就那麼一點,很快就能學完了,但是真正靈活的、熟練應用起來真的是不容易,這需要我們大量地分析、思考、練習。有時間的話我們也應該多讀讀一些關於軟件設計思想的書籍,這也是我最近在讀的一類書,學學前輩們總結出的一些精華知識。

以上就是本次關於回調函數的筆記分享,如有錯誤,歡迎指出。


分享到:


相關文章: