1. 程式人生 > >回撥指標回撥函式的使用(C語言)

回撥指標回撥函式的使用(C語言)

回撥函式的使用

回撥函式在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