1. 程式人生 > >函式指標作為某個函式的引數及定義函式指標(回撥函式)

函式指標作為某個函式的引數及定義函式指標(回撥函式)

轉載於:http://blog.csdn.net/vlily/article/details/7244682

轉載於:http://blog.csdn.net/shengnan_wu/article/details/8116935

轉載於:http://blog.csdn.net/callmeback/article/details/4242260/

一、 定義某一函式的指標型別: 就像自定義資料型別一樣,我們也可以先定義一個函式指標型別,然後再用這個型別來申明函式指標變數。 我先給你一個自定義資料型別的例子。 typedef int* PINT;    //為int* 型別定義了一個PINT的別名 int main() {   int x;   PINT px=&x;   //與int * px=&x;是等價的。PINT型別其實就是int * 型別   *px=10;       //px就是int*型別的變數     return 0; } 根據註釋,應該不難看懂吧!(雖然你可能很少這樣定義使用,但以後學習Win32程式設計時會經常見到的。) 下面我們來看一下函式指標型別的定義及使用:(請與上對照!) //自行包含標頭檔案 void MyFun(int x);    //此處的申明也可寫成:void MyFun( int ); typedef void (*FunType)(int );   //這樣只是定義一個函式指標型別 FunType FunP;              //然後用FunType型別來申明全域性FunP變數 int main(int argc, char* argv[]) { //FunType FunP;    //函式指標變數當然也是可以是區域性的 ,那就請在這裡申明瞭。     MyFun(10);         FunP=&MyFun;      (*FunP)(20);           return 0; } void MyFun(int x)   {    printf(“%d\n”,x); } 看黑體部分: 首先,在void (*FunType)(int ); 前加了一個typedef 。這樣只是定義一個名為FunType函式指標型別,而不是一個FunType變數。 然後,FunType FunP;  這句就如PINT px;一樣地申明一個FunP變數。 其它相同。整個程式完成了相同的事。 這樣做法的好處是: 有了FunType型別後,我們就可以同樣地、很方便地用FunType型別來申明多個同類型的函式指標變量了。如下: FunType FunP2; FunType FunP3; //……  二、 函式指標作為某個函式的引數

既然函式指標變數是一個變數,當然也可以作為某個函式的引數來使用的。所以,你還應知道函式指標是如何作為某個函式的引數來傳遞使用的。 給你一個例項: 要求:我要設計一個CallMyFun函式,這個函式可以通過引數中的函式指標值不同來分別呼叫MyFun1、MyFun2、MyFun3這三個函式(注:這三個函式的定義格式應相同)。 實現:程式碼如下: //自行包含標頭檔案  void MyFun1(int x);   void MyFun2(int x);   void MyFun3(int x);   typedef void (*FunType)(int ); //②. 定義一個函式指標型別FunType,與①函式型別一至 void CallMyFun(FunType fp,int x); int main(int argc, char* argv[]) {    CallMyFun(MyFun1,10);   //⑤. 通過CallMyFun函式分別呼叫三個不同的函式    CallMyFun(MyFun2,20);       CallMyFun(MyFun3,30);    } void CallMyFun(FunType fp,int x) //③. 引數fp的型別是FunType。 {   fp(x);//④. 通過fp的指標執行傳遞進來的函式,注意fp所指的函式是有一個引數的 } void MyFun1(int x) // ①. 這是個有一個引數的函式,以下兩個函式也相同 {    printf(“函式MyFun1中輸出:%d\n”,x); } void MyFun2(int x)   {    printf(“函式MyFun2中輸出:%d\n”,x); } void MyFun3(int x)   {    printf(“函式MyFun3中輸出:%d\n”,x); } 輸出結果:略  

  1. 函式指標是指向函式的指標變數。c在編譯時,每一個函式都有一個入口地址,該入口地址就是函式指標指向的地址處。有了指向函式的指標變數後。可以通過該指標變數呼叫函式,函式指標有兩個用途:呼叫函式、做函式的引數:

    1. 呼叫函式,如下所示:

輸出結果如下:

由以上可以說明成功呼叫。

   b.無參函式指標做引數的實現,如下(標準寫法)所示:

輸出結果如下:

還有以下寫法也能成功,因為c語言標準規定可以這樣用:

也能成功輸出

c.帶參有返回值的函式指標做引數的

輸出結果如下:

而不能寫成如下所示:

也可寫成以下形式,其中涉及到函式指標型別的轉換:

2.函式指標陣列的實用之處:當我們需要判斷大量條件的時候,並且在每一個條件都有相應的處理函式,這時實用switch...case..的程式碼量會很大,並且效率會比較低,這個時候就可以使用函式指標陣列來解決這個問題了,可以使用每個條件為陣列下表:如下所示:

結果如下

回撥函式

1、基礎知識

所謂回撥,就是模組A要通過模組B的某個函式b()完成一定的功能,但是函式b()自己無法實現全部功能,需要反過頭來呼叫模組A中的某個函式a()來完成,這個a()就是回撥函式。如下圖

①約定介面規範。在模組B必須約定介面規範,也就是定義回撥函式a()的函式原型

一開始是不好理解,用下面這個例子可能會有幫助: 諸葛亮(A)給趙子龍(B)一個錦囊(a()),吩咐他在幹活時(b())若遇到危急時開啟按錦囊(a())指示辦, 錦囊裡的命令就是回撥函式,危急時刻就是回撥的時機。  不同的錦囊裡可以有不同的命令。

在看LWIP時,見到用回撥函式,再看某老外公司OPC原始碼時,見到用回撥函式。看我國內某些程式碼(我公司軟體等)時沒用到。於是,我對回撥函式產生了很大的好奇。以前,我寫VC程式時用到過回撥函式,但是沒有用C語言來使用。最近,看到國外大量的經典程式碼中廣泛使用了回撥函式(LWIP、某兩個公司的OPC程式等),都是C語言來實現的,而不是VC windows程式中別人實現自己使用的那種。

為了弄明白這種函式的奧妙,首先提出三個問題:

1.        回撥函式是什麼東西?

2.        回撥函式怎麼開發,怎麼使用?

3.        回撥函式的作用,應該在什麼情況下使用?

帶著問題來學習,有目的!呵呵,個人經驗。

開啟baidu.com、google.cn搜尋了好多資料,如下:

順便提一下,某君的一個簽名很讓我佩服:1好好活著,因為我們會死很久。2五千年的文明 兩百年的無奈

第一個問題:

*******************************************************************************

其實回撥就是一種利用函式指標進行函式呼叫的過程.  

為什麼要用回撥呢?比如我要寫一個子模組給你用,   來接收遠端socket發來的命令.當我接收到命令後,   需要呼叫你的主模組的函式,   來進行相應的處理.但是我不知道你要用哪個函式來處理這個命令,     我也不知道你的主模組是什麼.cpp或者.h,   或者說,   我根本不用關心你在主模組裡怎麼處理它,   也不應該關心用什麼函式處理它......   怎麼辦?

使用回撥!

—— lone wolf

使用回撥函式實際上就是在呼叫某個函式(通常是API函式)時,將自己的一個函式(這個函式為回撥函式)的地址作為引數傳遞給那個函式。而那個函式在需要的時候,利用傳遞的地址呼叫回撥函式,這時你可以利用這個機會在回撥函式中處理訊息或完成一定的操作。

—— 某專家

回撥函式,就是由你自己寫的。你需要呼叫另外一個函式,而這個函式的其中一個引數,就是你的這個回撥函式名。這樣,系統在必要的時候,就會呼叫你寫的回撥函式,這樣你就可以在回撥函式裡完成你要做的事。

—— 綠葉

什麼是回撥函式?   回撥函式是應用程式提供給Windows系統DLL或其它DLL呼叫的函式,一般用於截獲訊息、獲取系統資訊或處理非同步事件。應用程式把回撥函式的地址指標告訴DLL,而DLL在適當的時候會呼叫該函式。回撥函式必須遵守事先規定好的引數格式和傳遞方式,否則DLL一呼叫它就會引起程式或系統的崩潰。通常情況下,回撥函式採用標準WindowsAPI的呼叫方式,即__stdcall,當然,DLL編制者可以自己定義呼叫方式,但客戶程式也必須遵守相同的規定。在__stdcall方式下,函式的引數按從右到左的順序壓入堆疊,除了明確指明是指標或引用外,引數都按值傳遞,函式返回之前自己負責把引數從堆疊中彈出。   理解回撥函式!

—— jufengfeng

Function Pointers provide the concept of callback functions.

—— newty.de

*******************************************************************************

看了這麼多的資料,我只將每位的定義總結一下就一句話:回撥函式就是函式指標的一種用法。

在部分資料上,大量討論了回撥函式怎麼被呼叫,到底被誰呼叫,還有好多的圖形,我認為都沒有看到問題的本質。

第二個問題:

*********************************************************************

我實現了一個很簡單的回撥函式。

#include <stdio.h>

void printWelcome(int len)

{

       printf("歡迎歡迎 -- %d/n", len);

}

void printGoodbye(int len)

{

       printf("送客送客 -- %d/n", len);

}

void callback(int times, void (* print)(int))

{

       int i;

       for (i = 0; i < times; ++i)

       {

              print(i);

       }

       printf("/n我不知道你是迎客還是送客!/n/n");

}

void main(void)

{

       callback(10, printWelcome);

       callback(10, printGoodbye);

       printWelcome(5);

}

*******************************************************************************

上面的程式碼沒有被任何系統函式呼叫,說明那些東西只是撒撒土迷迷路人眼而已。還有面相物件程式設計時,用class給封裝起來也是掩人耳目,不要被外表所迷惑。

第三個問題:

*********************************************************************

用過STL的人都知道,在STL中眾多演算法和程式都用到回撥函式,這實現了一種策略。只要任何符合我的標準的函式和計算都可以用我這個公式。你可以實現各種各樣的回撥函式,只要符合我的格式就能用。

就上面的程式來說,你只要函式格式符合cllback第二個引數的格式不論你給別人做飯、鋪床疊被都可以正常工作。這就是回撥的作用,把回撥實現留給別人。

這是一個用法。

有一位朋友用分層的概念來解釋了回撥機制:callback函式為B層,main函式和print*函式為A層,A層呼叫了B層的回撥函式callmeback,而B層的回撥函式呼叫了A層的實現函式print*。說白了B層就是一個介面。

這是我的理解。Over!