函數指針、指針函數以及回調函數

 

函數指針和指針函數

函數指針:

int func ( );

int (*func) ( );   //指針兩側的括號不能省略,表示先對func解引用,然後再調用相應的函數

函數指針是一個指向函數的指針, 其實就是函數, 因爲ANSI C標準將func ( )認爲是(*func)( ) 的簡寫形式 ,並且推薦使用func ( )形式,因爲它更符合函數調用的邏輯。
注:
但是我在測試的時候,定義爲int func ( );和定義爲int (*func) ( );還是有差別的,
int func ( );表示定義了一個函數
int (*func) ( );表示定義了一個指向函數的指針。

函數指針的使用:

void test ( );
int wrong_match (int);
void (*ptf) ( );

下面的初始化是錯誤的,因爲函數指針的類型與函數的類型 匹配:

test = wrong_match;

test = & wrong_match;

ptf = wrong_match;

ptf = & wrong_match;

 

以下初始化及賦值是合法的:

ptf = test;

ptf = &test;


C語言規定函數名會被轉換爲指向這個函數的指針,除非這個函數名作爲 & 操作符或sizeof操作符的操作數。
( 注意:函數名用於 sizeof 的操作數是非法的 )

也就是說,在
ptf = test;
中test被自動轉換爲&test。

而在
ptf = &test;
中已經顯示使用了&test,所以test就不會再發生轉換了。
因此直接引用函數名等效於在函數名上應用 & 運算符,兩種方法都會得到指向該函數的指針。

測試例程和運行結果:

pointer_to_func.c文件:
  1 #include <stdio.h>
  2
  3 int testfunc(void)
  4 {
  5     printf("%s: call me(%p)./n", __func__, testfunc);
  6     return 0;
  7 }
  8
  9 int main(int argc, char *argv[])
 10 {
 11     /* define a pointer to function */
 12     void (*func)(void);
 13     //void func(void);
 14
 15     func = (void *)testfunc;
 16
 17     printf(" testfunc(%p)/n",  testfunc);
 18     printf("&testfunc(%p)/n", &testfunc);
 19     printf("*testfunc(0x%x)/n", *testfunc);
 20
 21     /* run function */
 22     func();
 23     (*func)();
 24
 25     return 0;
 26 }

輸出結果:

[lhcX86@horizon tmp]$ make pointer_to_func
cc     pointer_to_func.c   -o pointer_to_func
[lhcX86@horizon tmp]$ ./pointer_to_func
 testfunc(0x8048374)
&testfunc(0x8048374)
*testfunc(0x8048374)
testfunc: call me(0x8048374).
testfunc: call me(0x8048374).

指針函數:

指針類型的函數,返回指針的函數。注意函數的返回值不僅僅侷限於指向變量的指針,也可以是指向函數的指針。
首先來看這個聲明:
int (* function(int) ) (double*, char);


要了解此聲明的含義,首先來看function(int),將function聲明爲一個函數,它帶有一個int型的形式參數,這個函數的返回值爲一個指針,正是我們本將開頭講過的函數指針 ── int (*) (double*, char);

這個指針指向一個函數,此函數返回int型並帶有兩個分別是double*型和char型的形參。如果使用typedef可以將這個聲明簡化:

定義一個 ptf 爲這個 函數指針的類型:
typedef  int (*ptf) (double*, char);

定義返回值類型爲ptf 函數指針的指針函數:
ptf  function( int );

要說明一下,對於typedef  int (*ptf) (double*, char);
注意:
不要用#define的思維來看待typedef,如果用#define的思維來看的話會以爲(*ptf)(double*, char)是int的別名,但這樣的別名看起來好像又不是合法的名字,於是會處於迷茫狀態。
實際上,上面的語句把ptf定義爲一種函數指針類型的別名,使得ptf和函數指針類型int (*) (double*, char);等價

回調函數

爲什麼要使用回調函數?

回調函數是一個程序員不能顯式調用的函數;
通過將回調函數的地址傳給 調用者從而實現調用。
在我們想通過一個統一函數接口實現不同的內容 ,這時用回掉函數非常合適。

什麼是回調函數?

  簡而言之,回調函數是一個通過函數指針調用的函數。如果你把函數指針(函數的入口地址)傳遞給另一個函數,當這個函數指針被用來調用它所指向的函數時,我們就說這個函數是回調函數。

例子:

下面時一個簡單的回調函數的例子,供理解:
callback.c
  1 /*
  2  * callback.c
  3  *
  4  *      4bsfreedom(at)gmail.com
  5  *  */
  6
  7 #include <stdio.h>
  8
  9 int func1(void)
 10 {
 11     printf("I'm callback function/n");
 12
 13     return 0;
 14 }
 15
 16 int func2(int x)
 17 {
 18     printf("func2 x is (%d)/n", x);
 19
 20     return 0;
 21 }
 22
 23 int func3(int x)
 24 {
 25     x = 2345;
 26     printf("do different thing in func3 (%d)/n", x);
 27
 28     return 0;
 29 }
 30
 31 void callback_test( int(*func)() )
 32 {
 33
 34     /* callback function here */
 35     (*func)();
 36 }
 37
 38 void callback_api( int(*func)(), int bb)
 39 {
 40
 41     /* callback function here */
 42     (*func)(bb);
 43 }
 44
 45 int main(int argc, char * argv[])
 46 {
 47     //printf("%d/n", argc);
 48     callback_test(func1);
 49     callback_api(func2, argc);
 50     callback_api(func3, argc);
 51
 52     return 0;
 53 }

運行結果:

[lhcX86@horizon tmp]$ make callback
cc     callback.c   -o callback
[lhcX86@horizon tmp]$ ./callback
I'm callback function
func2 x is (1)
do different thing in func3 (2345)


References:

《探討C語言中的回調函數》
http://www.blogjava.net/huyi2006/articles/180169.html

《異步消息的傳遞-回調機制》
http://www.ibm.com/developerworks/cn/linux/l-callback/index.html

《分清函數指針和指針函數》
http://blog.csdn.net/porscheyin/archive/2008/12/06/3461632.aspx

 

PS:

爲了加深自己的理解,和一些其它的C語言基本功,我最近查看了三本經典書籍來補充C語言知識,書名這裏與大家共享一下。

《C Traps and Pitfalls》(C陷阱與缺陷) Andrew Koenig 著

《The C Programming Language》(C程序設計) Dennis M.Ritchie 著

《高質量程序設計指南──C/C++》林銳 韓永泉 著

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章