C Function Pointer 基礎用法整理

function pointer

這最近在看 jserv 大神的 linux 核心設計講座,因為之前比較少寫 c,所以對於 function pointer 只有一點概念但沒有真正寫過,所以趁這個機會寫個筆記來紀錄一下用法。

之後在學習的過程中如果遇到 function pointer 的應用也會一併整理在這篇文章中。

function pointer

function pointer 顧名思義就是指向某個 functionpointer,有了 function pointer 我們就可以實現把 function 當作參數,傳進一個 function 之中,或者更加彈性的設計我們的程式,減少多餘的 if/else, switch case

我們先從一個簡單的 function pointer 宣告開始講起

1
int (*myFunc)(int, int);

上面就是一個基本的 function pointer 宣告

一個 function pointer 變數名稱為 myFunc,可以這麼解讀

  • myFunc 是一個指標指向一個 function
    • function 有兩個 int 的 parameters
    • function 會回傳 int

假設今天我有個 function 宣告成以下這種形式

1
void parseFunc(float f1, int i1, char c1);

我們要怎麼宣告一個 pointer 去指向這個 function 呢?

1
void (*myFunc)(float, int, char);

解讀成

  • myFunc 是一個指標指向一個 function
    • function 有三個 parameters,分別要傳入
      • float
      • int
      • char
    • function 會回傳 void

所以我們可以來驗證看看這個 function pointer 是否能真的呼叫 parseFunc 這個 function

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include <stdio.h>

void parseFunc(float f1, int i1, char c1) {
    printf("%f %d %c\n", f1, i1, c1);
}

int main() {
    parseFunc(0.87, 87, 'a');

    void (*myFunc)(float, int, char) = parseFunc;
    myFunc(0.87, 877, 'b');
    return 0;
}

程式執行結果

注意

上面在宣告 function pointer 的時候

1
void (*myFunc)(float, int, char)

會注意到 *myFunc 會用括號包起來,這是不能省略的喔,省略的話就不是 function pointer 的宣告方法了。

省略的話會變成

1
void *myFunc(float, int, char);

這樣子 myFunc 就不是 function pointer 了,而是單純宣告一個 function

Jserv 上課的範例

簡單的例子看完就可以來看看 Jserv你所不知道的C語言:指標篇 開頭所舉出來的範例

試試看能不能自己寫出該宣告怎麼解釋

1
void **(*d) (int &, char **(*)(char *, char **));
  • d 是一個 function pointer, 該 function 有兩個 parameters
    • 第一個 parameters: a reference to an int
    • 第二個 parameters: 同樣也是一個 function pointer
      • function 有兩個 parameters
        • a pointer to a char
        • a pointer to a pointer to a char
      • return a pointer to a pointer to a char
  • return a pointer to a pointer to void

應用舉例

今天如果我想寫一個 calculatefunction 該 function 有三個參數

  • a
  • b
  • 加減乘除

傳入 a, b 之後再傳入要計算的動作,我們可以利用 function pointer 來取代 switch/case, 並增加可讀性

我們可以宣告成

1
2
3
4
5
6
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }
int div(int a, int b) { return a / b; }

int calculate(int a, int b, int (*cal)(int, int));

用這樣的宣告形式,就可以把我們實現好的加減乘除 function 當作參數傳入 calculate 當中

但是又有人會說 calculate 的第三個參數宣告還是有點複雜,有沒有辦法可以再讓可讀性增加呢?

這時候我們會習慣用 typedef 的關鍵字把常用的 function pointer 宣告取一個比較簡短的名稱

導入 typedef 之後我們可以寫成以下的形式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
typedef int (*calc)(int, int);

int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }
int div(int a, int b) { return a / b; }

int calculate(int a, int b, calc method) 
{
    return method(a, b);
}

這樣子我們在看 calculate 宣告的時候就會清楚的知道第三個參數要把指定的 method 傳進去。

完整的程式碼我放在 GitHub,可以把 code 載下來自己 run 一次,順便改改看寫法,觀察其中的不同。

reference

CC BY-NC 4.0
使用 Hugo 建立
主題 StackJimmy 設計