函數指針
函數指針是指向函數調用地址的指針。它和函數名究竟有什么關系呢?且看下文。
看一小程序
首先,先請看下邊程序:
1 #include <iostream> 2 #include <string> 3 using namespace std; 4 5 void func(string s) 6 { 7 cout << s << endl; 8 } 9 10 void (*pFunc)(string s); // 不能夠寫成 void *pFunc(string s); 11 // 繼續看下文解釋 12 13 int main() 14 { 15 cout << endl; 16 func("Original Function : 1st circumstance!"); 17 (*func)("Original Function : 2nd circumstance!"); 18 19 cout << endl; 20 pFunc = &func; 21 (*pFunc)("Function pointer : 1st circumstance!"); 22 pFunc("Function pointer : 2nd circumstance!"); 23 24 cout << endl; 25 pFunc = func; 26 (*pFunc)("Function pointer : 3rd circumstance!"); 27 pFunc("Function pointer : 4th circumstance!"); 28 29 return 0; 30 }
程序的運行結果如下:
從上邊程序,我們可以得到下邊結論:
1. 函數指針和函數名本質上是一樣的,都是指向函數調用地址的指針,只是函數名是常量指針,函數指針是變量。
2. 調用函數的寫法func()、(*func)()均可,而我們大多數情況下都會寫成前者,應該是(C/C++標准制定者)為了方便大家對函數的調用。
博文Function Pointers and Callbacks in C — An Odyssey如此說:
A pointer is a special kind of variable that holds the address of another variable. The same concept applies to function pointers, except that instead of pointing to variables, they point to functions. If you declare an array, say, int a[10];
then the array name a
will in most contexts (in an expression or passed as a function parameter) “decay” to a non-modifiable pointer to its first element (even though pointers and arrays are not equivalent while declaring/defining them, or when used as operands of the sizeof
operator). In the same way, for int func();
, func
decays to a non-modifiable pointer to a function. You can think of func
as a const
pointer for the time being.
void (*pFunc)()與void* pFunc()區別
對於前者,我們都知道pFunc是函數指針變量,函數的返回值是空(void)。
對於后者而言,情況卻很不一樣。pFunc是函數指針常量,函數的返回值是void指針。
typedef void (*pFunc)()是什么意思?
發現自己看不懂這個語句了!!!(*pFunc)()是void的別名?這說不通啊!
typedef的語法如下:
typedef type_declaration;
為什么自己會看不懂,是因為自己一直把type_declaration分成兩個部分,后半部分是前半部分的別名。其實這是錯的!!!type_declaration一直是一個整體,而不是分成兩半!還得注意,typedef跟#define根本是兩回事!
在下邊的語句中,‘void (*pFunc)()’是完整的一個類型聲明,也就意味着pFunc不再是一個函數指針變量,而是一個函數指針類型。
typedef void (*pFunc)()
下邊是一個能夠說明typedef的簡單例子:
// simple typedef typedef unsigned long ulong; // the following two objects have the same type unsigned long l1; ulong l2; // more complicated typedef typedef int int_t, *intp_t, (&fp)(int, ulong), arr_t[10]; // the following two objects have the same type int a1[10]; arr_t a2; // common C idiom to avoid having to write "struct S" typedef struct {int a; int b;} S, *pS; // the following two objects have the same type pS ps1; S* ps2;
有了函數指針類型,以后我們就可以象變量一樣聲明函數指針,如下例:
1 #include <iostream> 2 using namespace std; 3 4 typedef void(*pFunc)(); 5 6 void myFunc() 7 { 8 cout << "Hello World!" << endl; 9 } 10 11 int main() 12 { 13 pFunc func; 14 func = &myFunc; 15 func(); 16 17 return 0; 18 }
回調函數
回調函數其實就是一個通過函數指針調用的函數!假如你把A函數的指針當作參數傳給B函數,然后在B函數中通過A函數傳進來的這個指針調用A函數,那么這就是回調機制。A函數就是回調函數,而通常情況下,A函數是系統在符合你設定條件的情況下會自動執行。
例如Linux下的多線程創建函數
#include <pthread.h> int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg); // 返回:若成功返回0,否則返回錯誤編號
中的 start_rtn 就是一個回調函數。
下邊是一個簡易的例子:
1 #include <iostream> 2 using namespace std; 3 4 typedef void (*callback)(int x); // 定義一個函數指針類型 5 6 void myFunc1(int x) // myFunc1 的聲明要與callback一樣 7 { 8 cout << "This is myFunc1 " << x << endl; 9 } 10 11 void myFunc2(int x) 12 { 13 cout << "This is myFunc2 " << x << endl; 14 } 15 16 void callMyFunc(callback cb, int x) // 把函數指針類型當做調用函數參數類型 17 { 18 cb(x); 19 } 20 21 int main() 22 { 23 callMyFunc(myFunc1, 1); 24 callMyFunc(myFunc2, 2); 25 26 return 0; 27 }
通過這個小程序可以看出,相對於普通函數調用來說,回調函數至少有如下優勢:
把回調函數定義成一個類型並作為調用函數的參數類型,這樣只要跟該回調函數類型有相同聲明而有不同實現的函數都可以被調用函數調用。這樣將極大擴展調用函數的功能。例如,我們可以定義N多個myFunc,而且互相之間實現各不相同,那對於callMyFunc來說就有N種功能。
關於回調函數詳細可參考知乎上問答回調函數(callback)是什么?。