Pointer - Function


前言

callback function 的概念對於程式初學者或是C不熟的讀者可能略顯陌生。但是這樣的技巧廣泛運用在各個地方, GUI、kernel、driver,或是各式演算法。尤其是 functional 的程式語言,如 javascript,python,Lisp...等等許多許多。一些標準的用法如 reduce,map,foreach 都見得到 callback function的影子。而在C中我們需要使用 function pointer 來實現這樣的概念。


這篇要說的是 functoin pointer. 顧名思義,指向 function 的指標。

考慮一個 function

char func(int x, int y)
{
    return (x + y) & 0xff;
}

這邊故意讓參數跟回傳不同型態以方面下面講解。

通常使用 function pointer都是為了實做 callback function。

一個 function 的型態由他的 prototype 決定,範例語法如下

char (*fp)(int, int) = func;

也就是

return_type (*fun_ptr_name)(args...)

給一個簡單的例子

int do_op(int (*op)(int,int), int x, int y)
{
    return (*op)(x, y);
}

int op_add(int x, int y)
{
    return x + y;
}

int op_mul(int x, int y)
{
    return x * y;
}

int main(int argc, const char * argv[])
{
    int add_val = do_op(op_add, 10, 20);
    int mul_val = do_op(op_mul, 10, 20);
}

我們從上面的例子去說明基本的 function pointer 使用

首先先從 do_op 開始。

我們可以看到這個 do_op 有三個參數,分別是 int (*op)(int, int), int xint y

可以注意到的是我們呼叫的時候是使用 (*op)(x,y) 這樣的方式。但事實上 function pointer使用時寫法很多

也有人會寫 op(x,y) 還有其他很特別的合法寫法(雖然真正寫code不會使用,如 (****op)(x,y)) 。我們這邊先不提這種特殊的,

就先就 (*op)(x,y)與 op(x,y) 比較而言。 雖然 op(x,y) 寫起來更像 "用 function" 也更簡潔,但是無法一眼看出該 function 為一個 function pointer。而 (*op)(x,y) 的寫法雖然看起來比較繁瑣一點點,但卻明確指出 op 為一個 function pointer。 這部分就端看團隊 coding style。

-

op_add 以及 op_mul 我們通常稱做 callback function。詳細的說明可以再去找其他資料,在此就僅介紹名詞。
而我們在使用 do_op 時就會將 op_add 以及 op_mul 當作參數傳入。
在邏輯上的說明是 - do_op 是一個二元運算的框架,因此要傳入一個二元運算子以及兩個運算元,回傳用該運算子運算的結果。

一個有名的使用是 qsort 以及 compare function。

qsort


void qsort(void *base, size_t nel, size_t width, int (*compar)(const void *, const void *))

稍微介紹一下參數 -
假設一個抽象型態 T, 有陣列 T a[20] 要由小到大排序。

  • base : 要排序的 array,在這邊是 a
  • nel : number of elements,此例為 20
  • width: 一個元素的大小,此例為 sizeof(T)
  • compar: compare function,定義大小。 應符合下列要求實做 -

在使用時,若 x, y 為型態 T 的變數,且 ret = compar(&x, &y)
ret < 0 若 x < yret == 0 若 x == y, ret > 0 若 x > y

給一個實際例子

#include <stdlib.h>

int cmp(const void * x, const void * y)
{
    return *(const int *)x - *(const int*)y;
}

int main(int argc, const char * argv[])
{
    int a[5] = {2, 4, 1, 6, 7};
    qsort(a, 5, sizeof(int),  cmp);
}

這邊說明一下 cmp 的寫法,原本的型態是 const void* 因此要先轉型成 const int*。 轉型後再取值,簡寫成 *(const int*)x。相減兩數就自然符合上述所規範的條件。要注意的是,這是剛好整數所以有一個自然的減法,若是一般型態(如某個自定義 struct)就不會有"減法",取而代之的可能是該structure的某個數值 member。

results matching ""

    No results matching ""