(c/c++) Function Pointer函式指標兩三事 (Function Pointer 的 typedef 與 Array of Function Pointer)

 Function Pointer

定義:

Function Pointer (中文直譯「函式指標」),即為儲存某一個函式起始memory address的變數,此變數可以提供我們在之後進行呼叫。

乍聽之下,function pointer就只是多一個別名再呼叫,似乎沒什麼實質的用處,但其實我們可以藉由function pointer省去繁複的 if/switch,後面會一一介紹。

使用方法:

 現在假設我們有以下這個函式:

int Square(int n)
{
    return n*n;
}

然後宣告一個function pointer變數,以便於指向函式Square:

int (*fptr)(int);
fptr = Square; 

這邊有幾個小細節必須注意:

1.最前面的int是變數data type(資料型態),和要指向的函式回傳值型態相同。第一個小括號代表指標變數名稱,第二個小括號代表傳入的parameter資料型態們,且理所當然的type必須與我們要指向的函式傳入值相同。

2. function pointer 的 pointer operator(*)必須與變數名稱一起被小括號括起來並接參數的小括號。若沒有這個小括號,會變成以下:

int *fptr(int);

在意義上,表示fptr為一個"函式"而不是變數,回傳的資料型態為 int* ,會有3種情況;

    a. 回傳一個pointer of int(整數指標變數的指標) -> int *a; return a;

    b. 回傳一個address of int(整數一般變數的位址) -> int a; return &a;

    c. 回傳一個int array(整數型態的陣列) -> int a[100]; return a;

延伸:

在知道function pointer的妙用前,還必須介紹以下兩種功能:

1.typedef

typedef是c/c++中的關鍵字,其允許programmer為data type(資料型態)創造一個全新的名字。同時也可以為函式創造一個別名,其好處在於若要把function當做一參數傳入另外一fumction時,可以讓該參數簡略化。這邊將其和function pointer做結合:

int Mul(int a, int b)
{
    return a*b;
} 

int *FuncA(int x, int y, int (*Opt)(int, int))
{
     return Opt(x, y);
} 
 
int result = FuncA(5, 10, Mul);    // 第三個參數可以傳入更多不同的計算方法,因為是function pointer

觀察FunctA,第三個帶入參數是一個function pointer,只是長的有點醜,就可以用typedef做簡化,如下:

typedef int (*MathMethod)(int, int);    // 注意*要在()之中
 
int Mul(int a, int b)
{
    return a*b;
}

int *FuncA(int x, int y, MathMethod Opt)
{
     return Opt(x, y);
} 
 
int result = FuncA(5, 10, Mul); 

以上簡化是連同回傳型態也簡化,同時作為新的型態(別名)可供使用,可以理解為把

int (*)(int, int)

簡化成

MathMethod

 但注意,function的typedef,不能寫成以下:

typedef int (*)(int, int) MathMethod;

呈上,配合typedef就可以讓各種擁有不同功能function(上面是實做乘法,也可以增加除法等)更直觀的當做參數傳入其他function。

typedef int (*MathMethod)(int, int); 
 
int Mul(int a, int b) { return a*b; }
int Divide(int a, int b) { return a/b; }
int Minus(int a, int b) { return a-b; }
int Add(int a, int b) { return a+b; } 

int *Calc(int x, int y, MathMethod Opt){
     return Opt(x, y);
} 
 
int main()
{
    int a = 8, b = 7;
    printf("a x b = %d\n", Calc(a, b, Mul)); 
    printf("a / b = %d\n", Calc(a, b, Divide));
    printf("a + b = %d\n", Calc(a, b, Minus));
    printf("a - b = %d\n", Calc(a, b, Add));
}

2. Array of Function Pointer

以上方法可以透過array of function pointer(指標函式陣列),讓工作更加簡便。所謂的array of function pointer是一個儲存function pointer的array,因此可以透過一些自訂的條件(例如輸入),去取得想要呼叫的function使用。光說不練假把戲,直接上例子。

typedef int (*MathMethod)(int, int); 

int Mul(int a, int b) { return a*b; }
int Divide(int a, int b) { return a/b; }
int Minus(int a, int b) { return a-b; }
int Add(int a, int b) { return a+b; } 

int (*calcArray[])(int, int) =   // Array of function pointer,存放會使用到的function名稱
{ 
    Mul,     // 這邊亦可以打&Mul,&可省略
    Divide, 
    Minus, 
    Add 
};
char* operation[] =   //做為判斷的自訂條件,與argv比對用
{
    "x",
    "/",
    "-",
    "+"
};

int main(int argc, char** argv)
{
    int size = sizeof(calcArray) / (sizeof(int(*)));    // 使用此方法可以避免每次增加功能時修改長度
    int i = 0;

    if(argc > 1)
        for(i; i<size; i++)
        {
            if( strcmp(argv[2], operation[i]) == 0)
            {
                int a = atoi(argv[1]), b = atoi(argv[3]);
                printf("%d %s %d = %d\n", a, argv[2], b, calcArray[i](a, b));
                break;
            }
        }
}

執行如下圖:

以上特別適用於很多條件且傳入參數相近時使用,若要新增新功能時,只要加上新的函式,並將其加入Array中,並加進自己的條件做判斷條件即可。

又例如不同按鍵有不同的操作函式(似乎BBS的鍵盤輸入操作就是使用這個方式去實現),就可以省去大量的if/switch的statement,讓程式可讀性與維護更加簡易。


留言

  1. 感謝草神的分享,讓我對函式指標有另一層面的認識
    我在執行您的程式碼時,有發現一些筆誤的地方
    原版
    int *Calc(int x, int y, MathMethod Opt){
    return Opt(x, y);
    }
    ---------------------------------------------------------------------
    修改後
    int Calc(int x, int y, MathMethod Opt){
    return Opt(x, y);
    }

    回覆刪除
  2. 同上
    int *FuncA(int x, int y, MathMethod Opt)
    {
    return Opt(x, y);
    }
    ---------------------------------------------------------------------
    修改後
    int FuncA(int x, int y, MathMethod Opt)
    {
    return Opt(x, y);
    }

    回覆刪除

張貼留言

這個網誌中的熱門文章

(C) 簡單搞懂指標(pointer)、指標陣列(pointers of array, int *foo[]) 與指向陣列的指標 (pointer to array, int (*bar)[])

(C++) 別再用dynamic array與pointer了! 趕快學STL的vector與iterator!