回呼函式無法由程式設計師直接呼叫,必須將其位址傳遞給呼叫端。函式指標正是實現此機制的關鍵——它儲存函式的進入點位址,允許執行期動態綁定。
一般函式宣告:void f();
對應函式指標型別:void (*)();
* 標識「指標」。void 為回傳型別,右側 () 為參數列。sizeof 或 typedef。// 取得函式指標大小
unsigned psize = sizeof(void (*)());
// 定義型別別名
typedef void (*pfv)(); // pfv:指向無參、回傳 void 的函式指標
// 宣告指標變數
void (*p)(); // p 為指標變數名
void func() { /* ... */ }
p = func; // 賦值:函式名即位址
p(); // 透過指標呼叫
(*p)(); // 等價寫法
賦值的函式必須簽章完全相符(回傳型別、參數型別、順序、呼叫慣例)。
void caller(void (*ptr)()) { // 接受函式指標參數
ptr(); // 呼叫被指向的函式
}
int main() {
p = func;
caller(p); // 動態綁定:執行期決定呼叫哪個函式
}
若 p 在執行期賦予不同函式位址,caller 將呼叫不同函式,實現多型行為。
呼叫慣例決定:參數壓棧順序(左→右/右→左)、堆疊清理責任(呼叫端/被呼叫端)、參數傳遞機制(堆疊/暫存器)、名稱修飾。常見慣例:
__cdecl(C/C++ 預設,呼叫端清理堆疊,支援可變參數)__stdcall(Win32 API 標準,被呼叫端清理堆疊)__fastcall(優先用暫存器傳參)__pascal(左→右壓棧,被呼叫端清理)
__stdcall int callee(int); // 被呼叫端用 stdcall
void caller(__cdecl int (*ptr)(int)); // 呼叫端期望 cdecl 指標
__cdecl int (*p)(int) = callee; // 錯誤:型別不相容!
// 雖然回傳/參數皆為 int,但呼叫慣例不同,屬不同型別
鐵律:呼叫慣例是函式型別的一部分。指標型別與目標函式的慣例必須嚴格一致,否則無法賦值。
本文僅涵蓋普通函式指標。類別成員函式指標語法、語義、大小皆不同(涉及 this 指標調整、虛函式表查找等),將另文探討。
回傳型別 (*指標名)(參數列),善用 typedef 簡化。__stdcall 或 __cdecl)。