這篇文章主要講解了“c語(yǔ)言中的回調(diào)函數(shù)怎么使用”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“c語(yǔ)言中的回調(diào)函數(shù)怎么使用”吧!
創(chuàng)新互聯(lián)專注于潮陽(yáng)企業(yè)網(wǎng)站建設(shè),自適應(yīng)網(wǎng)站建設(shè),購(gòu)物商城網(wǎng)站建設(shè)。潮陽(yáng)網(wǎng)站建設(shè)公司,為潮陽(yáng)等地區(qū)提供建站服務(wù)。全流程按需定制網(wǎng)站,專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)
掌握程序架構(gòu)的核心理念或需求。掌握回調(diào)函數(shù)的作用掌握回調(diào)函數(shù)的程序編寫(xiě)掌握回調(diào)函數(shù)在產(chǎn)品中的應(yīng)用
很多人可能會(huì)說(shuō)一個(gè)好的程序架構(gòu)啊,就是代碼很緊湊、執(zhí)行效率也很高。
其實(shí)這個(gè)說(shuō)的很片面,不完全對(duì),這只能說(shuō)明你程序算法寫(xiě)的好,但架構(gòu)不一定做的好。
即然是架構(gòu),那自然是以從”大局”為重,思維不能局限于當(dāng)下的產(chǎn)品功能,還要考慮到以后功能的增加和裁剪,那么對(duì)于單片機(jī)開(kāi)發(fā)來(lái)說(shuō),我認(rèn)為一個(gè)好的程序架構(gòu)至少要達(dá)到以下要求:
硬件層和應(yīng)用層的程序代碼分開(kāi),相互之間的控制和通訊使用接口,而且不會(huì)共享的全局變量或數(shù)組。
這里呢,我就這個(gè)要求,別小看這一個(gè)要求,因?yàn)檫@個(gè)要求里面蘊(yùn)藏著很多學(xué)問(wèn)的,比如用專業(yè)稱為可移植性、可擴(kuò)展性。
那么我們來(lái)想象一下我們通常寫(xiě)單片機(jī)代碼的方式啊,在51的時(shí)候基本一個(gè).c文件解決,包括寄存器配置啊,產(chǎn)品功能啊。
這種就是沒(méi)有架構(gòu)的程序,然后我們進(jìn)化到STM32這個(gè)單片機(jī)以后,程序大了,慢慢也會(huì)在工程文件里加幾個(gè)文件夾目錄把硬件層和應(yīng)用層代碼分開(kāi)了。
于是我們會(huì)把一些不同的外設(shè)功能,比如Led、按鍵、串口等外設(shè)功能代碼分別寫(xiě)在不同的.c文件里,然后統(tǒng)一用函數(shù)接口去調(diào)用它。
比方說(shuō)控制一個(gè)LED燈亮,直接在led.c文件里寫(xiě)一個(gè)驅(qū)動(dòng)led燈狀態(tài)的函數(shù)然后給外部調(diào)用就好了。
那我們我們看這種Led的控制函數(shù)確實(shí)也是滿足程序架構(gòu)的需求的,硬件層和應(yīng)用層代碼分開(kāi),應(yīng)用層用硬件層提供的接口來(lái)控制,而且又不會(huì)有硬件層和應(yīng)用層共享的全部變量或數(shù)組。像這種是不是很簡(jiǎn)單?
那么不知道你們有沒(méi)有碰到另外一種情況,就是應(yīng)用程序需要采集硬件層的數(shù)據(jù),比如串口接收數(shù)據(jù),按鍵采集、ADC值采集。
這種硬件層的數(shù)據(jù)怎么通知應(yīng)用層來(lái)拿,或者怎么主動(dòng)給它?
我們以往最簡(jiǎn)單粗暴的方式是不是就是用一個(gè)全局變量,比方說(shuō)硬件層串口接收到數(shù)據(jù)來(lái)了,那么我們把數(shù)據(jù)丟到數(shù)組里,然后把接收完成全局變量標(biāo)志位置1。
比方說(shuō)全局變量名為RcvFlag,然后應(yīng)用層程序會(huì)輪詢判斷RcvFlag==1?是的話就開(kāi)始把數(shù)組里的數(shù)據(jù)取出來(lái)解析。
很多人就會(huì)說(shuō)了,你看我用這種方法照樣能實(shí)現(xiàn)功能啊,為什么還要學(xué)習(xí)別的架構(gòu)。
這樣做當(dāng)然可以實(shí)現(xiàn)功能,但是會(huì)存在移植性很差的問(wèn)題。
比如說(shuō)你們老板讓你把這個(gè)串口的硬件層封裝起來(lái)給客戶用,但不能讓客戶看到你實(shí)現(xiàn)的源代碼,只提供接口(函數(shù)名)給對(duì)方用。
那么這時(shí)候難道你要告訴客戶先判斷哪個(gè)變量為1,然后再取哪個(gè)數(shù)組的數(shù)據(jù)這么LOW的做法嗎?
那么如果是懂行的客戶一定會(huì)懷疑你們公司的技術(shù)實(shí)力是不是小學(xué)生水平。
那怎樣做才會(huì)既方便又專業(yè)呢? 這里我們就需要用到回調(diào)函數(shù)啦。
那么在講回調(diào)函數(shù)之前呢,對(duì)于函數(shù)調(diào)用呢我一般分為2種類(lèi)型:
不知道大家有沒(méi)有用過(guò)C語(yǔ)言自帶的一些庫(kù)函數(shù),比如說(shuō)sizeof()獲取數(shù)據(jù)長(zhǎng)度的函數(shù),memcpy()是內(nèi)存拷貝函數(shù),我們調(diào)用這個(gè)函數(shù)之后呢就能完成相應(yīng)的功能。
還有我們基于單片機(jī)的一些程序函數(shù),比方說(shuō)控制LED點(diǎn)亮熄滅、繼電器吸合斷開(kāi)、LCD驅(qū)動(dòng)等等。
那么這些呢,我一般稱為輸出型的函數(shù)。
輸出型函數(shù)我們是主導(dǎo)的角色,我們知道什么時(shí)候該調(diào)用它。
輸入型呢,也稱為的是響應(yīng)式的函數(shù)。
什么叫響應(yīng)式的函數(shù)呢?
比方說(shuō)接收串口的數(shù)據(jù),我們不知道什么數(shù)據(jù)什么時(shí)候來(lái)。
再比方說(shuō),我們按鍵檢測(cè)的函數(shù),我們不知道什么時(shí)候會(huì)按下按鍵,那么這些就要定義成響應(yīng)式函數(shù)來(lái)實(shí)現(xiàn),而響應(yīng)式函數(shù)就可以用回調(diào)函數(shù)來(lái)實(shí)現(xiàn)。
所以通過(guò)這兩個(gè)種類(lèi)型的分析啊,我們就可以知道,回調(diào)函數(shù)基本是用在輸入型的處理中。
比方說(shuō)串口數(shù)據(jù)接收,那么數(shù)據(jù)是輸入到單片機(jī)里面的,單片機(jī)是處于從機(jī)角色。
按鍵檢測(cè),按鍵狀態(tài)是輸入到單片機(jī)里的。
再比方說(shuō)ADC值采集,ADC值也是輸入到單片機(jī)里的。
那么它們輸入的時(shí)間節(jié)點(diǎn)都是未知的,這些就能夠用回調(diào)函數(shù)來(lái)處理。
具體怎么處理后面我們會(huì)用代碼來(lái)給大家舉例。
回調(diào)函數(shù)還有一個(gè)作用就是為了封裝代碼。
比如說(shuō)做芯片或者模組的廠家,我們拿典型的STM32來(lái)舉例,像外部中斷、定時(shí)器、串口等中斷函數(shù)都是屬于回調(diào)函數(shù),這種函數(shù)的目的是把采集到的數(shù)據(jù)傳遞給用戶,或者說(shuō)應(yīng)用層。
所以回調(diào)函數(shù)的核心作用是:
1.把數(shù)據(jù)從一個(gè).c文件傳遞到另一個(gè).c文件,而不用全局變量共享數(shù)據(jù)這么LOW的方法。
2.對(duì)于這種數(shù)據(jù)傳遞方式,回調(diào)函數(shù)更利于代碼的封裝。
前面說(shuō)了很多概念性的東西,可能大家也比較難理解,回調(diào)函數(shù)最終呢是靠函數(shù)指針來(lái)實(shí)現(xiàn)的。
那么我這里通過(guò)一些模擬按鍵的例子來(lái)演示下怎么回通過(guò)調(diào)函數(shù)來(lái)處理它們。
下面是我們的c-free工程,用這個(gè)來(lái)模擬方便點(diǎn):
從模塊化編程的思想來(lái)看,整個(gè)工程分為2個(gè)部分,應(yīng)用層main.c文件,硬件層key.c和key.h文件。
不管再怎么復(fù)雜的程序,我們都要先從main函數(shù)一步步往下挖,main函數(shù)代碼如下。
int main(int argc, char *argv[]) { KeyInit(); KeyScanCBSRegister(KeyScanHandle); KeyPoll(); return 0; }
KeyInit();是key.c文件的按鍵初始化函數(shù)
KeyScanCBSRegister(KeyScanHandle);是key.c的函數(shù)指針注冊(cè)函數(shù)。
這個(gè)函數(shù)可能大家會(huì)有點(diǎn)蒙,請(qǐng)跟進(jìn)我們的節(jié)奏,下面開(kāi)始燒腦環(huán)節(jié),也是寫(xiě)回調(diào)函數(shù)的必須步驟,
想理解這個(gè)回調(diào)函數(shù)注冊(cè)函數(shù),我們要先從硬件層(key.h)頭文件的函數(shù)指針定義說(shuō)起,具體看下圖。
這里自定義了一個(gè)函數(shù)指針類(lèi)型,帶兩個(gè)形參。
然后,我們?cè)趉ey.c這個(gè)文件里定義了一個(gè)函數(shù)指針變量。
重點(diǎn)來(lái)了,我們就是通過(guò)這個(gè)函數(shù)指針,指向應(yīng)用層的函數(shù)地址(函數(shù)名)。
具體怎么實(shí)現(xiàn)指向呢?就是通過(guò)函數(shù)指針注冊(cè)函數(shù)。
這個(gè)函數(shù)是在main函數(shù)里調(diào)用,使用這種注冊(cè)函數(shù)的方式注冊(cè)靈活性也很高,你想要在哪個(gè).c文件使用按鍵功能就在哪里調(diào)用。
這里要注意,main.c這個(gè)文件要定義一個(gè)函數(shù)來(lái)接收硬件層(key.c)過(guò)來(lái)的數(shù)據(jù)。
這里定義也不是亂定義的,一定要和那個(gè)自定義函數(shù)指針類(lèi)型返回值、形參一致。
然后把這個(gè)函數(shù)名字直接復(fù)制給KeyScanCBSRegister函數(shù)的形參就可以了。
這樣調(diào)用后,我們key.c文件的pKeyScanCBS這個(gè)指針其實(shí)就是指向的KeyScanHandle函數(shù)。
也就是說(shuō)執(zhí)行pKeyScanCBS的時(shí)候,就是執(zhí)行KeyScanHandle函數(shù)。
那具體檢測(cè)按鍵的功能就是KeyPoll函數(shù),這個(gè)在main函數(shù)里調(diào)用。
當(dāng)檢測(cè)到鍵盤(pán)有輸入以后,最終會(huì)調(diào)用pKeyScanCBS。
最終執(zhí)行的是main.c文件的KeyScanHandle函數(shù)。
所以,我們來(lái)看下輸出結(jié)果。
如果還是有點(diǎn)模糊,下面我再給大家捋一捋編寫(xiě)和使用回調(diào)函數(shù)的流程:
自定義函數(shù)指針,形參作為硬件層要傳到應(yīng)用層的數(shù)據(jù)。
硬件層定義一個(gè)函數(shù)指針和函數(shù)指針注冊(cè)函數(shù)。
應(yīng)用層定義一個(gè)函數(shù),返回值和形參都要和函數(shù)指針一致。
應(yīng)用層調(diào)用函數(shù)指針注冊(cè)函數(shù),把定義好的函數(shù)名稱作為形參傳入。
感謝各位的閱讀,以上就是“c語(yǔ)言中的回調(diào)函數(shù)怎么使用”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)c語(yǔ)言中的回調(diào)函數(shù)怎么使用這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!