回撥指標回撥函式的使用(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