1. 程式人生 > >函式指標和賦值 回撥函式

函式指標和賦值 回撥函式

問:先定義了個函式:
int function(int a, int b)
{
     ..........
}

main函式裡面有個函式指標
問題1:那麼下面兩種對函式指標的賦值哪個是正確的,還是都可以?
int main(void)
{
     A:     int (*FP)(int, int) = function;
     B:     int (*FP)(int, int) = &function;
}

問題2:使用FP這個函式指標的時候,下面哪種是正確的,還是都正確?
A:     FP(1,2);
B:     (*FP)(1,2);

解釋為什麼?
答:
第一個問題,兩個都是可以的。函式名是一個地址,可以將他賦值給一個指向函式的指標。前面加了&符號其意義是一樣的。比如定義一個數組arr[],arr表示這個陣列的首地址,但&arr同樣表示他的首地址。這些都是設計語言時這樣規定的
第二個問題,兩個也都是可以的,比如你定義一個字串指標char *str, 輸出str 他並不是輸出了地址,而是輸出存在該地址的值,所有這些設計都是為了方便使用,而不是為了符合同一個規則。

回撥函式也是函式指標的一點使用


 今天討論下C/C++中的回撥函式。

     在理解“回撥函式”之前,首先討論下函式指標的概念。

函式指標

(1)概念:指標是一個變數,是用來指向記憶體地址的。一個程式執行時,所有和執行相關的物件都是需要載入到記憶體中,這就決定了程式執行時的任何物件都可以用指標來指向它。函式是存放在記憶體程式碼區域內的,它們同樣有地址,因此同樣可以用指標來存取函式,把這種指向函式入口地址的指標稱為函式指標。

(2)先來看一個Hello World程式:

int main(int argc,char* argv[])
{
    printf("Hello World!\n");
    return 0;
}

       然後,採用函式呼叫的形式來實現:

複製程式碼
void Invoke(char* s);

int main(int argc,char* argv[])
{
    Invoke("Hello World!\n");
    return 0;
}

void Invoke(char* s)
{
    printf(s);
}
複製程式碼

      用函式指標的方式來實現:

複製程式碼
void Invoke(char* s);

int main()
{
    void (*fp)(char* s);    //宣告一個函式指標(fp)            fp=Invoke;              //將Invoke函式的入口地址賦值給fp
fp("Hello World!\n"); //函式指標fp實現函式呼叫 return 0;
} void Invoke(char* s) { printf(s); }
複製程式碼

      由上知道:函式指標函式的宣告之間唯一區別就是,用指標名(*fp)代替了函式名Invoke,這樣這聲明瞭一個函式指標,然後進行賦值fp=Invoke就可以進行函式指標的呼叫了。宣告函式指標時,只要函式返回值型別、引數個數、引數型別等保持一致,就可以宣告一個函式指標了。注意,函式指標必須用括號括起來 void (*fp)(char* s)。

     實際中,為了方便,通常用巨集定義的方式來宣告函式指標,實現程式如下:

複製程式碼
typedef void (*FP)(char* s);
void Invoke(char* s);

int main(int argc,char* argv[])
{
    FP fp;      //通常是用巨集FP來宣告一個函式指標fp    fp=Invoke;
    fp("Hello World!\n");
    return 0;
}

void Invoke(char* s)
{
    printf(s);
}
複製程式碼

函式指標陣列

      下面用程式對函式指標陣列來個大致瞭解:

複製程式碼
#include <iostream>
#include <string>
using namespace std;

typedef void (*FP)(char* s);
void f1(char* s){cout<<s;}
void f2(char* s){cout<<s;}
void f3(char* s){cout<<s;}

int main(int argc,char* argv[])
{
    void* a[]={f1,f2,f3};   //定義了指標陣列,這裡a是一個普通指標    a[0]("Hello World!\n"); //編譯錯誤,指標陣列不能用下標的方式來呼叫函式
    FP f[]={f1,f2,f3};      //定義一個函式指標的陣列,這裡的f是一個函式指標    f[0]("Hello World!\n"); //正確,函式指標的陣列進行下標操作可以進行函式的間接呼叫    
    return 0;
}
複製程式碼

回撥函式

(1)概念:回撥函式,顧名思義,就是使用者自己定義一個函式,使用者自己實現這個函式的程式內容,然後把這個函式作為引數傳入別人(或系統)的函式中,由別人(或系統)的函式在執行時來呼叫的函式。函式是你實現的,但由別人(或系統)的函式在執行時通過引數傳遞的方式呼叫,這就是所謂的回撥函式。簡單來說,就是由別人的函式執行期間來回調你實現的函式。

(2)標準Hello World程式:

int main(int argc,char* argv[])
{
    printf("Hello World!\n");
    return 0;
}

      將它修改成函式回撥樣式:

複製程式碼
//定義回撥函式void PrintfText() 
{
    printf("Hello World!\n");
}

//定義實現回撥函式的"呼叫函式"void CallPrintfText(void (*callfuct)())
{
    callfuct();
}

//在main函式中實現函式回撥int main(int argc,char* argv[])
{
    CallPrintfText(PrintfText);
    return 0;
}
複製程式碼

      修改成帶參的回撥樣式:

複製程式碼
//定義帶參回撥函式void PrintfText(char* s) 
{
    printf(s);
}

//定義實現帶參回撥函式的"呼叫函式"void CallPrintfText(void (*callfuct)(char*),char* s)
{
    callfuct(s);
}

//在main函式中實現帶參的函式回撥int main(int argc,char* argv[])
{
    CallPrintfText(PrintfText,"Hello World!\n");
    return 0;
}
複製程式碼

      至此,對回撥函式應該有了一個大致的瞭解。