複雜的函式呼叫回傳

當我們在呼叫函式為我們做工作的時候,通常都只是給個陣列或是參數等等,麻煩的地方就大約是指標的型別問題,但這邊我們講到同時具有傳入函式指標、參數、陣列且回傳函式指標等,非常複雜的函式原型。這篇值得對於C/C++的進階程式設計者閱讀。

※ 複雜函式原型

這邊假設讀者已對於C/C++有一定的水準,所以對於函式指標或是陣列等等,這邊不討論,這邊只單純的討論以下的複雜函式原型。

Example:

int (*func_a(int, void (*)(char**, int (*)[10])))(const char*, int);

我想大概一輩子也不會去看到這種宣告吧!但是這邊來討論若遇到如此複雜的函式原型我們該如何拆解和簡化,若直接一看眼看出來的人應該是高手中的高手,但我相信應該沒有幾個人。

1. func_a(int, void (*)(char**, int (*)[10]))

這邊可以看到func_a是這個函式主要的部份,但又可以從它第二個參數拆出void (*)(char**, int(*)[10])這表示傳入的為一個函式而這個函式有兩個參數,第一個參數是傳入一個指向字串陣列的指標或是字元雙指標,第二個參數是傳入一個指向二維陣列指標,且此二維陣列的第二個維度為10(point to array of 10 elements)。

Example:

void fn1(char** str_array, int (*p)[10])
{
//empty
}
void (*fnptr)(char**, int (*)[10]) = fn1; //讓fnptr函式指標指向fn1
func_a(10, fn1) ;
func_a(20, fnptr);

2. int (*FNA)(const char*, int)

我們將上一步的整個部份看成是FNA這個職別字,現在單看這行,我們可以很容易了解,它是一個指向一個函式的指標且具有兩個參數,第一個參數是常數字串,第二個是個整數,而且這函式會回傳int型別的資料。

Example:

int fn2(const char* str, int size)
{
//empty
}
int (*FNA)(const char* str, int size) = fn2; //讓FNA函式指標指向fn2

由上式可以得知FNA會回傳一個指標指向型為int (*)(const char*, int)的函式指標。

3. 結合上面1和2

就是呼叫 func_a 需要傳入int和一個函式的位址,且回傳為一個函式的位址,用範例來看會比較清楚。

Example:

void fn1(char** str_array, int (*p)[10])
{
//empty
}
int fn2(const char* str, int size)
{
//empty
}
int (*func_a(int a, void (*fnptr)(char**, int (*)[10])))(const char* , int)
{
char* str_ary[2] = {"abc", "cde"};
int int_ary[10][10] = {0};
(*fnptr)(str_ary, int_ary); //叫用傳入的函式做運算
return fn2; //回傳函式
}
int main()
{
int (*fnp)(const char*, int) = func_a(10, fn1); //呼叫func_a回傳函式用fnp去指向此函式
int sum = (*fnp)("test", 20); //透過回傳函式指標來使用函式
return 0;
}

※ 善用typedef

使用typedef可以讓很複雜的式子簡化,增加閱讀功能,寫一個大型的程式一定非常需要使用到,所以我們這裡將上面很複雜的函式原型簡化。

Example:

typedef void (*fnptr)(char**, int (*)[10]);
typedef int (*FNA)(const char* str, int size);

那很複雜的原型宣告會變成FNA func_a(int size, fnptr ptr)這就很容易看的懂了,傳入需要int和一個fnptr的型別,回傳一個FNA的型別。

不過函式指標這麼麻煩的我還沒有用過,有用到簡單的,是做選擇的功能,如標準函式庫就有qsort, bsearch要用到函式指標,其實函式指標可以讓程式寫的更隨心所欲。

0 意見: