宣告函式指標並實現回撥 (轉)

worldblog發表於2007-12-05
宣告函式指標並實現回撥 (轉)[@more@]宣告指標並實現回撥    

員常常需要實現回撥。本文將討論函式指標的基本原則並說明如何使用函式指標實現回撥。注意這裡針對的是普通的函式,不包括完全依賴於不同語法和語義規則的類成員函式(類成員指標將在另文中討論)。

宣告函式指標

   回撥函式是一個程式設計師不能顯式的函式;透過將回撥函式的地址傳給呼叫者從而實現呼叫。要實現回撥,必須首先定義函式指標。儘管定義的語法有點不可思議,但如果你熟悉函式宣告的一般方法,便會發現函式指標的宣告與函式宣告非常類似。請看下面的例子:

void f();// 函式原型

上面的語句宣告瞭一個函式,沒有輸入引數並返回void。那麼函式指標的宣告方法如下:

void (*) ();

   讓我們來分析一下,左邊圓括弧中的星號是函式指標宣告的關鍵。另外兩個元素是函式的返回型別(void)和由邊圓括弧中的入口引數(本例中引數是空)。注意本例中還沒有建立指標變數-只是宣告瞭變數型別。目前可以用這個變數型別來建立型別定義名及用sizeof獲得函式指標的大小:

// 獲得函式指標的大小
unsigned psize = sizeof (void (*) ());

// 為函式指標宣告型別定義
typedef void (*pfv) ();

pfv是一個函式指標,它指向的函式沒有輸入引數,返回類行為void。使用這個型別定義名可以隱藏複雜的函式指標語法。

指標變數應該有一個變數名:

void (*p) (); //p是指向某函式的指標

   p是指向某函式的指標,該函式無輸入引數,返回值的型別為void。左邊圓括弧裡星號後的就是指標變數名。有了指標變數便可以賦值,值的內容是署名匹配的函式名和返回型別。例如:

void func()
{
/* do something */
}
p = func;

p的賦值可以不同,但一定要是函式的地址,並且署名和返回型別相同。

傳遞迴調函式的地址給呼叫者

   現在可以將p傳遞給另一個函式(呼叫者)- caller(),它將呼叫p指向的函式,而此函式名是未知的:

void caller(void(*ptr)())
{
ptr(); /* 呼叫ptr指向的函式 */
}
void func();
int main()
{
p = func;
caller(p); /* 傳遞函式地址到呼叫者 */
}

   如果賦了不同的值給p(不同函式地址),那麼呼叫者將呼叫不同地址的函式。賦值可以發生在執行時,這樣使你能實現動態繫結。

呼叫規範

   到目前為止,我們只討論了函式指標及回撥而沒有去注意ANSI C/C++的規範。許多編譯器有幾種呼叫規範。如在Visual C++中,可以在函式型別前加_cdecl,_stdcall或者_pascal來表示其呼叫規範(預設為_cdecl)。C++ Builder也支援_fastcall呼叫規範。呼叫規範影響編譯器產生的給定函式名,引數傳遞的順序(從右到左或從左到右),堆疊清理責任(呼叫者或者被呼叫者)以及引數傳遞機制(堆疊,暫存器等)。

   將呼叫規範看成是函式型別的一部分是很重要的;不能用不相容的呼叫規範將地址賦值給函式指標。例如:

// 被呼叫函式是以int為引數,以int為返回值
__stdcall int callee(int);

// 呼叫函式以函式指標為引數
void caller( __cdecl int(*ptr)(int));

// 在p中企圖被呼叫函式地址的操作
__cdecl int(*p)(int) = callee; // 出錯


   指標p和callee()的型別不相容,因為它們有不同的呼叫規範。因此不能將被呼叫者的地址賦值給指標p,儘管兩者有相同的返回值和引數列。  




























來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-988569/,如需轉載,請註明出處,否則將追究法律責任。

相關文章