深入淺出剖析C語(yǔ)言函數(shù)指針與回調(diào)函數(shù)(一)
今天我們要搞明白的一個(gè)概念叫回調(diào)函數(shù)。
什么是回調(diào)函數(shù)?
百度的權(quán)威解釋如下:
回調(diào)函數(shù)就是一個(gè)通過(guò)函數(shù)指針調(diào)用的函數(shù)。如果你把函數(shù)的指針(地址)作為參數(shù)傳遞給另一個(gè)函數(shù),當(dāng)這個(gè)指針被用來(lái)調(diào)用其所指向的函數(shù)時(shí),我們就說(shuō)這是回調(diào)函數(shù)?;卣{(diào)函數(shù)不是由該函數(shù)的實(shí)現(xiàn)方直接調(diào)用,而是在特定的事件或條件發(fā)生時(shí)由另外的一方調(diào)用的,用于對(duì)該事件或條件進(jìn)行響應(yīng)。
那么我們可以來(lái)看一個(gè)例子:
#include <stdio.h>
void print();
int main(void)
{
void (*fuc)();
fuc = print ;
fuc();
}
void print()
{
printf("hello world!\n");
}
從這個(gè)例子可以看到,我們首先定義了一個(gè)函數(shù)指針fuc ,這個(gè)函數(shù)指針的返回值為void型,然后我們給函數(shù)指針賦值,賦值為print,也就是print函數(shù)的首地址,此時(shí)fuc獲得了print的地址,fuc的地址等于print的地址,所以最終調(diào)用fuc();也就相當(dāng)于調(diào)用了print();
那 么我寫(xiě)的這個(gè)例子明顯和百度解釋的不符合啊?定義是如果你把函數(shù)的指針(地址)作為參數(shù)傳遞給另一個(gè)函數(shù),當(dāng)這個(gè)指針被用來(lái)調(diào)用其所指向的函數(shù)時(shí),我們就說(shuō)這是回調(diào)函數(shù),確實(shí),有所不同,但道理是一樣的,我們接下來(lái)再來(lái)看一個(gè)例子。
#include <stdio.h>
int add_ret() ;
int add(int a , int b , int (*add_value)())
{
return (*add_value)(a,b);
}
int main(void)
{
int sum = add(3,4,add_ret);
printf("sum:%d\n",sum);
return 0 ;
}
int add_ret(int a , int b)
{
return a+b ;
}
從這個(gè)例子里,我們看到:
這樣子不就符合我們的定義了嘛?我們把函數(shù)的指針(地址),這里也就是add_ret,作為參數(shù)int add(int a , int b , int (*add_value)()) , 這里的參數(shù)就是int(*add_value)() , 這個(gè)名字可以隨便取,但是要符合C語(yǔ)言的命名規(guī)范。當(dāng)這個(gè)指針被用來(lái)調(diào)用其所指向的函數(shù)時(shí),我們就說(shuō)這是回調(diào)函數(shù)。我們看到add函數(shù)內(nèi)部,return (*add_value)(a,b) ; 這個(gè)(*add_value)(a,b)相當(dāng)于對(duì)指針進(jìn)行了簡(jiǎn)引用,我們?cè)趍ain函數(shù)中,傳入具體要實(shí)現(xiàn)功能的函數(shù),add_ret,這個(gè)函數(shù)很簡(jiǎn)單,就是實(shí)現(xiàn)兩數(shù)相加并返回,這里剛剛好,簡(jiǎn)引用,相當(dāng)于取出指針?lè)祷氐刂防锏闹担@個(gè)值就是return a+b,也就是我們傳入a和b兩數(shù)相加的結(jié)果。
那么,回調(diào)函數(shù)究竟有什么作用呢?
說(shuō)到這里,就有了用戶和開(kāi)發(fā)者之間的概念,比方說(shuō),剛剛說(shuō)的add()這個(gè)函數(shù),假設(shè)一下,用戶是實(shí)現(xiàn)add_value這個(gè)函數(shù),而開(kāi)發(fā)者是實(shí)現(xiàn)add_value這個(gè)函數(shù),用戶做的工作不多,就是想要通過(guò)開(kāi)發(fā)者實(shí)現(xiàn)的這么一個(gè)接口,然后在函數(shù)中通過(guò)調(diào)用開(kāi)發(fā)者實(shí)現(xiàn)的這個(gè)接口的返回值,然后來(lái)實(shí)現(xiàn)我們的功能。這個(gè)開(kāi)發(fā)者角色就很多了,可以是自己公司的核心開(kāi)發(fā)人物,也可以是別的工作的外包商的人物,這時(shí)候,他作為一個(gè)開(kāi)發(fā)者的角色完完全全可以將add_value實(shí)現(xiàn)的add_ret這個(gè)函數(shù)封裝起來(lái)并且加密,然后扔一個(gè).so或者.a給用戶,那么用戶就看不到具體add_ret的實(shí)現(xiàn)內(nèi)容,用戶只需要開(kāi)發(fā)者給他提供一個(gè).h和.so即可,這樣,作為開(kāi)發(fā)者,他就將他實(shí)現(xiàn)的函數(shù)功能給保密了。
接下來(lái),我們用Linux來(lái)演示下這個(gè)結(jié)果:
我們?cè)谀夸浵聞?chuàng)建三個(gè)文件,main.c,vendor.c,vendor.h
Main.c是用戶開(kāi)發(fā)的
Vendor.c和vendor.h是開(kāi)發(fā)者實(shí)現(xiàn)的。
在main.c中,代碼如下:
#include <stdio.h>
#include "vendor.h"
int add(int a , int b , int (*add_value)())
{
return (*add_value)(a,b);
}
int main(void)
{
int sum = add(3,4,add_ret);
printf("sum:%d\n",sum);
return 0 ;
}
vendor.c,代碼如下:
#include "vendor.h"
int add_ret(int a , int b)
{
return a+b ;
}
vendor.h,代碼如下:
#ifndef __VENDOR_H
#define __VENDOR_H
int add_ret(int a, int b) ;
#endif
接下來(lái),我們制作一個(gè)動(dòng)態(tài)鏈接庫(kù),最終開(kāi)發(fā)者把vendor.c的內(nèi)容封起來(lái),把vendor.h提供給用戶使用。
在linux下制作動(dòng)態(tài)鏈接庫(kù),將vendor.c和vendor.h打包成一個(gè)動(dòng)態(tài)鏈接庫(kù)
先明白以下幾個(gè)命令是什么意思:
生成動(dòng)態(tài)庫(kù):
gcc -shared -fPIC dvendor.c -o libvendor.so
-shared : 生成動(dòng)態(tài)庫(kù);
-fPIC : 生成與位置無(wú)關(guān)代碼;
-o :指定生成的目標(biāo)文件;
使用動(dòng)態(tài)庫(kù):
gcc main.c -L . –lvendor -o main
-L : 指定庫(kù)的路徑(編譯時(shí)); 不指定就使用默認(rèn)路徑(/usr/lib/lib)
-lvendor : 指定需要?jiǎng)討B(tài)鏈接的庫(kù)是誰(shuí);
代碼運(yùn)行時(shí)需要加載動(dòng)態(tài)庫(kù):
./main 加載動(dòng)態(tài)庫(kù) (默認(rèn)加載路徑:/usr/lib /lib ./ ...)
./main
我們將編譯動(dòng)態(tài)庫(kù)生成的libvendor.so拷貝到/usr/lib后,現(xiàn)在就不需要vendor.c了,此時(shí)我們將vendor.c移除,也可以正常的編譯并且執(zhí)行main函數(shù)的結(jié)果,這就是回調(diào)函數(shù)的作用之一。
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!