C語言中回調(diào)函數(shù)的含義與使用場景詳解
舉例
在下述程序中函數(shù) test2_cal()
中調(diào)用 函數(shù)指針 s_cal 指定的函數(shù)
執(zhí)行數(shù)值的計(jì)算。則 s_cal 指定的那些函數(shù)
就可以看作一個(gè)回調(diào)函數(shù)。
typedef int (*my_calculate_t)(int a, int b); static int cal_sum(int a, int b) { printf("now is sum\r\n"); return a + b; } static int cal_sub(int a, int b) { printf("now is sub\r\n"); return a - b; } static int cal_mul(int a, int b) { printf("now is mul\r\n"); return a * b; } static my_calculate_t s_cal = cal_sum; static int test2_cal (int a, int b) { int result = 0; if(s_cal) { result = s_cal(a ,b); printf("result=%d\r\n", result); } return result; } void app_main(void) { printf("init done\r\n"); int m = 10, n = 1, ret; ret = test2_cal(m, n); }
上述程序中 s_cal 的值為 cal_sum()
,函數(shù) cal_sum()
就是一個(gè)回調(diào)函數(shù);即當(dāng)前要執(zhí)行的運(yùn)算為 cal_sum
定義的加法運(yùn)算。
當(dāng)前程序的輸出結(jié)果:
init done
now is sum
result=11
也可以改變 s_cal 的值為 cal_sub
,cal_mul
,它們分別對應(yīng)減法、乘法運(yùn)算。讀者可自行賦值進(jìn)行測試。
小結(jié):從上述測試,我們不難理解,僅僅通過更改一個(gè) s_cal
函數(shù)指針的值分別指向cal_sum
,cal_sub
,cal_mul
就可以實(shí)現(xiàn)整個(gè)程序運(yùn)行不同的運(yùn)算。運(yùn)算的接口被統(tǒng)一為 test2_cal()
,它具備了執(zhí)行多種運(yùn)算的功能(通過更改s_cal
函數(shù)指針的值指定其功能)。另外,從該示例中,對于回調(diào)函數(shù),我們還認(rèn)識(shí)到它往往具有一個(gè)外殼
,如本例中的 test2_cal()
就是外殼,和一個(gè)核心
,即函數(shù)指針,如本例中的 s_cal
函數(shù)指針。
回調(diào)對于編寫庫文件有很大的好處,比如我們要實(shí)現(xiàn)一個(gè)加法,但加法分很多種:整數(shù)的加法、字符串的加法、指針的加法等等。我們可以定義一個(gè)統(tǒng)一的 add()
,并在 add()
中定義一個(gè)函數(shù)指針s_add
,通過更改s_add
所指的函數(shù),來適應(yīng)多種數(shù)據(jù)類型的加法。這在C++ 中被稱為“多態(tài)”,即根據(jù)輸入的數(shù)據(jù)類型,調(diào)用符合該數(shù)據(jù)類型運(yùn)算的函數(shù)。
同樣的,在編寫驅(qū)動(dòng)程序時(shí),由于不同的設(shè)備具備不同的特性,初始化時(shí)的內(nèi)容可能不一樣。使用回調(diào)函數(shù),可以通過改變對應(yīng)的函數(shù)指針的值,來指向不同的設(shè)備的初始化函數(shù),實(shí)現(xiàn)能夠兼容許多設(shè)備的驅(qū)動(dòng)程序。
動(dòng)態(tài)改變回調(diào)函數(shù)的實(shí)現(xiàn)的方法:
如上所示,回調(diào)函數(shù)是通過函數(shù)指針來調(diào)用的。因此想改變回調(diào)函數(shù)的功能,就是研究如何改變函數(shù)指針的值。主要有以下三種方法:
1)編譯時(shí)直接賦值
如上一節(jié)所示的示例,通過對函數(shù)指針 s_cal
賦值,可以改變函數(shù) test2_cal()
實(shí)際運(yùn)行的計(jì)算。這在編譯時(shí)就知道要將函數(shù)指針 s_cal
賦予的值的情況下,可以使用。若在編譯的時(shí)候不知道具體要將 s_cal
賦予什么值,或者需要程序運(yùn)行時(shí)動(dòng)態(tài)地改變 s_cal
的值,直接賦值的方法無法正常使用。
2)運(yùn)行時(shí)實(shí)現(xiàn)動(dòng)態(tài)注冊
運(yùn)行時(shí),可以通過其他函數(shù)對函數(shù)指針進(jìn)行賦值,在程序運(yùn)行的時(shí)候,動(dòng)態(tài)地改變函數(shù)的行為。
示例:
typedef int (*my_calculate_t)(int a, int b); static int cal_sum(int a, int b) { printf("now is sum\r\n"); return a + b; } static int cal_sub(int a, int b) { printf("now is sub\r\n"); return a - b; } static int cal_mul(int a, int b) { printf("now is mul\r\n"); return a * b; } static my_calculate_t s_cal = cal_sum; static int test2_cal (int a, int b) { int result = 0; if(s_cal) { result = s_cal(a ,b); printf("result=%d\r\n", result); } return result; } static void my_cal_calculate_register(my_calculate_t cal) { s_cal = cal; } static void my_cal_calculate_unregister(void) { s_cal = NULL; } void app_main(void) { printf("init done\r\n"); int m = 10, n = 2, ret; ret = test2_cal(m, n); my_cal_calculate_register(cal_sub); ret = test2_cal(m, n); my_cal_calculate_unregister(); my_cal_calculate_register(cal_mul); ret = test2_cal(m, n); my_cal_calculate_unregister(); }
運(yùn)行結(jié)果:
init done
now is sum
result=12
now is sub
result=8
now is mul
result=20
小結(jié):上述程序通過函數(shù) my_cal_calculate_register()
動(dòng)態(tài)地改變函數(shù)指針s_cal
的值,從而實(shí)現(xiàn)在函數(shù)中動(dòng)態(tài)地改變test2_cal
功能的目的。一些庫文件的源代碼是不開放的,因此,一些庫中使用這種通過動(dòng)態(tài)注冊函數(shù)的方法來動(dòng)態(tài)地指定庫函數(shù)實(shí)際功能。
3)作為函數(shù)參數(shù)傳遞到指定的函數(shù)內(nèi)
typedef int (*my_calculate_t)(int a, int b); static int cal_sum(int a, int b) { printf("now is sum\r\n"); return a + b; } static int cal_sub(int a, int b) { printf("now is sub\r\n"); return a - b; } static int cal_mul(int a, int b) { printf("now is mul\r\n"); return a * b; } static int test1_cal (my_calculate_t actual_cal, int a, int b) { int result = 0; result = actual_cal(a ,b); printf("result=%d\r\n", result); return result; } void app_main(void) { printf("init done\r\n"); int m = 10, n = 2, ret; ret = test1_cal(cal_sum, m, n); ret = test1_cal(cal_sub, m, n); ret = test1_cal(cal_mul, m, n); }
運(yùn)行結(jié)果:
init done
now is sum
result=12
now is sub
result=8
now is mul
result=20
小結(jié):上面的示例在調(diào)用 test1_cal() 時(shí),通過傳遞要運(yùn)行的運(yùn)算函數(shù),實(shí)現(xiàn)同一個(gè)函數(shù)test1_cal
執(zhí)行多個(gè)功能。同名函數(shù)傳遞不同參數(shù)執(zhí)行不同的功能,也是增強(qiáng)函數(shù)兼容性的常見方法。
總結(jié)
本篇文章重點(diǎn)簡述了回調(diào)、回調(diào)函數(shù)的概念。本質(zhì)上,回調(diào)函數(shù)是 C 語言中 函數(shù)指針
的一種用法。回調(diào)函數(shù)用于在統(tǒng)一的接口內(nèi),實(shí)現(xiàn)可以動(dòng)態(tài)地改變函數(shù)的功能。
回調(diào)函數(shù)的實(shí)現(xiàn)至少需要兩個(gè)函數(shù),一個(gè)是外殼,一個(gè)是可以通過函數(shù)指針指定實(shí)際的被執(zhí)行的函數(shù)。
動(dòng)態(tài)改變回調(diào)函數(shù)的實(shí)現(xiàn)的方法主要有三種:
- 編譯時(shí)直接賦值
- 運(yùn)行時(shí)實(shí)現(xiàn)動(dòng)態(tài)注冊
- 作為函數(shù)參數(shù)傳遞到指定的函數(shù)內(nèi)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
最短時(shí)間學(xué)會(huì)基于C++實(shí)現(xiàn)DFS深度優(yōu)先搜索
常見使用深度優(yōu)先搜索(DFS)以及廣度優(yōu)先搜索(BFS)這兩種搜索,今天我們就來講講什么是深度優(yōu)先搜索,感興趣的可以了解一下2021-08-08Android App仿微信界面切換時(shí)Tab圖標(biāo)變色效果的制作方法
這篇文章主要介紹了Android App仿微信界面切換時(shí)Tab圖標(biāo)變色效果的制作方法,重點(diǎn)講解了圖標(biāo)的繪制技巧,需要的朋友可以參考下2016-04-04C語言單鏈隊(duì)列的表示與實(shí)現(xiàn)實(shí)例詳解
這篇文章主要介紹了C語言單鏈隊(duì)列的表示與實(shí)現(xiàn),對于研究數(shù)據(jù)結(jié)構(gòu)與算法的朋友來說很有參考借鑒價(jià)值,需要的朋友可以參考下2014-07-07C++實(shí)現(xiàn)LeetCode(108.將有序數(shù)組轉(zhuǎn)為二叉搜索樹)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(108.將有序數(shù)組轉(zhuǎn)為二叉搜索樹),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07C++中構(gòu)造函數(shù)與析構(gòu)函數(shù)的調(diào)用順序詳解
C++ 語言一直被批評太復(fù)雜了,很多細(xì)節(jié)的地方需要仔細(xì)推敲,甚至其構(gòu)造函數(shù)和析構(gòu)的調(diào)用順序也成為了一個(gè)讓人迷惑的問題,在此我做了簡單的總結(jié)。這篇文章主要介紹了C++中構(gòu)造函數(shù)與析構(gòu)函數(shù)的調(diào)用順序,需要的朋友可以參考借鑒。2017-01-01