回調函數使用詳解

轉載自: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


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章