浅谈函数指针
?函數指針的概念,在潭浩強先生的C語言程序設計這本經典的教程中提及過,在大多數情況下我們使用不到,也忽略了它的存在。函數名實際上也是一種指針,指向函數的入口地址,但它又不同于普通的如int*、double*指針,看下面的例子來理解函數指針的概念:
int function( int x, int y ); int main ( void ) {int (*fun) ( int x, int y );int a = 10, b = 20;function( a, b );fun = function;(*fun)( a, b );...... }第一行代碼首先定義了一個函數function,其輸入為兩個整型數,返回也為一個整型數(輸入參數和返回值可為其它任何數據類型);后面又定義了一個函數指針fun,與int*或double*定義指針不同的是,函數指針的定義必須同時指出輸入參數,表明這是一個函數指針,并且*fun也必須用一對括號括起來;并將函數指針賦值為函數function,前提條件是*fun和function的輸入參數和返回值必須保持一致,否則無法通過編譯。可以直接調用函數function(),也可以直接調用函數指針,二者是等效的。
聲明函數指針
??? 回調函數是一個程序員不能顯式調用的函數;通過將回調函數的地址傳給調用者從而實現調用。要實現回調,必須首先定義函數指針。盡管定義的語法有點不可思議,但如果你熟悉函數聲明的一般方法,便會發現函數指針的聲明與函數聲明非常類似。請看下面的例子:
void f();??? // 函數原型
上面的語句聲明了一個函數,沒有輸入參數并返回void。那么函數指針的聲明方法如下:
void (*) ();
函數存放在內存的代碼區域內,它們同樣有地址,我們如何能獲得函數的地址呢?
如果我們有一個int test(int a)的函數,那么,它的地址就是函數的名字,這一點如同數組一樣,數組的名字就是數組的起始地址。
定義一個指向函數的指針用如下的形式,以上面的test()為例:
int (*fp)(int a);???? //這里就定義了一個指向函數的指針
函數指針絕對不能指向不同類型,或者是帶不同形參的函數,在定義函數指針的時候我們很容易犯如下的錯誤。
int *fp(int a);????? //這里是錯誤的,因為按照結合性和優先級來看就是先和()結合,然后變成了一個返回整形指針的函數了,而不是函數指針,這一點尤其需要注意!
例如函數原型為:
int? fun(int *, int);
則函數指針可以聲明為:??? int (*pf)(int *, int);
當然從上述例子看不出函數指針的優點,目的主要是想引出函數指針數組的概念。我們從上面例子可以得知,既然函數名可以通過函數指針加以保存,那們也一定能定義一個數組保存若干個函數名,這就是函數指針數組。正確使用函數指針數組的前提條件是,這若干個需要通過函數指針數組保存的函數必須有相同的輸入、輸出值。
如果賦了不同的值給函數指針,那么調用者將調用不同地址的函數。賦值可以發生在運行時,這樣使你能實現動態綁定。
下面我們來看一個具體的例子:
int test(int a) { return a; } int main(void) { int (*fp)(int a);fp = test; //將函數test的地址賦給函數指針fp cout<<fp(5)<<"|"<<(*fp)(10)<<endl; //輸出fp(5),這是標準c++的寫法,(*fp)(10)這是兼容c語言的標準寫法,兩種同意,但注意區分,避免寫的程序產生移植性問題!return 0; }typedef定義可以簡化函數指針的定義,在定義一個的時候感覺不出來,但定義多了就知道方便了,上面的代碼改寫成如下的形式:
int test(int a) { return a; } int main(void) {typedef int (*fp)(int a); //注意,這里不是生命函數指針,而是定義一個函數指針的類型,這個類型是自己定義的,類型名為fp fp fpi; //這里利用自己定義的類型名fp定義了一個fpi的函數指針! fpi = test; cout<<fpi(5)<<"|"<<(*fpi)(10)<<endl; //輸出fp(5),這是標準c++的寫法,(*fp)(10)這是兼容c語言的標準寫法,兩種同意,但注意區分,避免寫的程序產生移植性問題!return 0; }
?
?
總結
- 上一篇: 2011.10.17百度面试题
- 下一篇: 微策略2011校园招聘笔试题(找出数组中