轉載自:https://blog.csdn.net/miao19920101/article/details/75648491
回調函數的使用
回調函數在C語言中是通過函數指針來實現的,通過將回調函數的地址傳給被調函數從而實現回調。因此,要實現回調,必須首先定義函數指針。
1. 回調指針
概念:指針是一個變量,是用來指向內存地址的。一個程序運行時,所有和運行相關的物件都是需要加載到內存中,這就決定了程序運行時的任何物件都可以用指針來指向它。函數是存放在內存代碼區域內的,它們同樣有地址,因此同樣可以用指針來存取函數,把這種指向函數入口地址的指針稱爲函數指針。
1. 採用函數調用的一般形式
首先看一個hello world!的程序:
int application_start( void )
{
OSStatus err = kNoErr;
char *s ="hello world !";
app_log(" s:%s",s);
return err;
}
打印的結果是:
[0][TCP: main.c: 90] s:hello world !
如果採用函數調用的形式來實現:
//聲明
void Islog( char *s);
int application_start( void )
{
OSStatus err = kNoErr;
Islog("hello world !");
return err;
}
void Islog( char *s){
app_log(" s:%s",s);
}
打印的結果是:
[0][TCP: main.c: 90] s:hello world !
2. 簡單的函數指針的應用
形式1:返回類型(*函數名)(參數表)
把上面的例子改成使用簡單的函數指針的寫法:
//聲明
void Islog( char *s);
int application_start( void )
{
OSStatus err = kNoErr;
void (*fp)(char *s);//聲明一個函數指針(fp)
fp = Islog;//將Islog的函數入口地址付給fp
fp("hello world !");//函數指針fp實現函數調用
return err;
}
void Islog( char *s){
app_log(" s:%s",s);
}
打印的結果是:
[0][TCP: main.c: 90] s:hello world !
由上知道:函數指針函數的聲明之間唯一區別就是:用指針名(*fp)代替了函數名Islog,這樣聲明瞭一個函數指針,然後進行賦值fp= Islog 就可以進行指針函數的調用了,聲明函數指針時,只要返回值類型、參數個數、參數類型等保持一致,就可以聲明一個函數指針了。注意,函數指針必須括號括起來 void (*fp)(char *s)。
3. 使用typedef更簡單
實際中,爲了方便,通常使用宏定義的方式聲明函數指針。
形式2:返回類型(*新類型)(參數表)
typedef void (*intFunc)(int);
此宏定義的意思是要定義的類型是void (*)(int),即參數一個int,什麼也不返回的函數指針,定義的別名是intFunc。
採用宏定義的方式聲明函數指針將上上面的代碼改寫:
//宏定義
typedef void(*FP)(char *s);
//聲明
void Islog( char *s);
int application_start( void )
{
OSStatus err = kNoErr;
FP fp; //通常使用宏FP來聲明一個函數指針fp
fp = Islog;//將Islog的函數入口地址付給fp
fp("hello world !");//函數指針fp實現函數調用
return err;
}
void Islog( char *s){
app_log(" s:%s",s);
}
打印的結果是:
[0][TCP: main.c: 90] s:hello world !
4. 函數指針數組
下面的是指針函數數組的例子:
例子1:
//宏定義
typedef void(*FP)(char *s);
//聲明
void Islog1( char *s);
void Islog2( char *s);
void Islog3( char *s);
int application_start( void )
{
OSStatus err = kNoErr;
//void* Islog[] = {Islog1,Islog2,Islog3};//定義了指針數組,這裏a是一個普通指針
//Islog3[0] ("hello world !");//編譯錯誤,指針數組不能用下標的方式來調用函數
FP Islog[] = {Islog1,Islog2,Islog3};//定義一個函數指針的數組,這裏的f是一個函數指針
Islog[0] ("hello world!");
Islog[1] ("hello world!");
Islog[2] ("hello world!");
return err;
}
void Islog1( char *s){app_log(" s:%s",s);}
void Islog2( char *s){app_log(" s:%s",s);}
void Islog3( char *s){app_log(" s:%s",s);}
打印的結果是:
[0][TCP: main.c: 101] s:hello world!
[4][TCP: main.c: 103] s:hello world!
[7][TCP: main.c: 105] s:hello world!
例子2:
//宏定義
typedef void(*FP)( char *s,int count);
//聲明
void Islog1( char *s,int count);
void Islog2( char *s,int count);
void Islog3( char *s,int count);
int application_start( void )
{
OSStatus err = kNoErr;
//void* Islog[] = {Islog1,Islog2,Islog3};//定義了指針數組,這裏a是一個普通指針
//Islog3[0] ("hello world !");//編譯錯誤,指針數組不能用下標的方式來調用函數
FP Islog[] = {Islog1,Islog2,Islog3};//定義一個函數指針的數組,這裏的f是一個函數指針
Islog[0] ("hello world!",1);
Islog[1] ("hello world!",2);
Islog[2] ("hello world!",3);
return err;
}
void Islog1( char *s,int count){app_log(" s:%s and count:%d",s,count);}
void Islog2( char *s,int count){app_log(" s:%s and count:%d",s,count);}
void Islog3( char *s,int count){app_log(" s:%s and count:%d",s,count);}
打印的結果是:
[0][TCP: main.c: 101] s:hello world! and count:1
[5][TCP: main.c: 102] s:hello world! and count:2
[9][TCP: main.c: 103] s:hello world! and count:3
2. 回調函數
概念:回調函數,顧名思義,就是使用者自己定義一個函數,使用者自己實現這個函數的程序內容,然後把這個函數作爲參數傳入別人(或系統)的函數中,由別人(或系統)的函數在運行時來調用的函數。函數是你實現的,但由別人(或系統)的函數在運行時通過參數傳遞的方式調用,這就是所謂的回調函數。簡單來說,就是由別人的函數運行期間來回調你實現的函數。再來看看來自Stack Overflow某位大神簡潔明瞭的表述:A “callback” is any function that is called by another function which takes the first function as a parameter。 也就是說,函數 FuncA 調用函數 FuncB)的時候,函數 FuncA通過參數給 函數 FuncB傳遞了另外一個函數 FuncC 的指針,在函數 FuncB 執行的過程中,函數FuncB 調用了函數 FuncC,這個動作就叫做回調(Callback),而先被當做指針傳入、後面又被回調的函數 F3 就是回調函數。到此應該明白回調函數的定義了吧?
我們將一開始的hello world函數修改成函數回調樣式:
1. 簡單的回調函數
//定義回調函數
void PrintText(){
app_log("hello world!");
}
//定義實現回調函數的“調用函數”
void CallprintfText(void (*callfunc)()){
callfunc();
}
int application_start( void )
{
OSStatus err = kNoErr;
CallprintfText(PrintText);
return err;
}
打印的結果是:
[0][TCP: main.c: 82] hello world!
2. 使用typedef寫法:
typedef void (*CallFunc)();
//定義回調函數
void PrintText(){
app_log("hello world!");
}
//定義實現回調函數的“調用函數”
void CallprintfText(CallFunc callfunc){
callfunc();
}
int application_start( void )
{
OSStatus err = kNoErr;
CallprintfText(PrintText);
return err;
}
打印的結果是:
[0][TCP: main.c: 82] hello world!
3. 修改帶參數的回調函數寫法
這裏只放了一個類型的參數,很重要!
void PrintText(char *s){
app_log("s:%s",s);
}
//定義實現回調函數的“調用函數”
void CallprintfText(void (*callfunc)(char*),char *s){
callfunc(s);
}
int application_start( void )
{
OSStatus err = kNoErr;
CallprintfText(PrintText,"hello world!");
return err;
}
打印的結果是:
[0][TCP: main.c: 82] hello world!
4. 使用typedef寫法:
參數類型兩種
typedef void (*CallFunc)(char *s,int count);
//定義回調函數
void PrintText(char *s,int count){
app_log("s:%s",s);
app_log("count:%d",count);
}
//定義實現回調函數的“調用函數”
void CallprintfText(CallFunc callfunc,char *s,int count){
callfunc(s,count);
}
int application_start( void )
{
OSStatus err = kNoErr;
CallprintfText(PrintText,"hello world!",1);
return err;
}
打印的結果是:
[0][TCP: main.c: 84] s:hello world!
[3][TCP: main.c: 85] count:1