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