定義:
被調者回頭調用調用著的函數,這種由調用方自己提供的函數叫回呼函數
應用情境舉例:
現有一個快速排序演算法,實現了快排演算法的邏輯,但是快排演算法中必須涉及資料大小的比較,為提高程式的通用性,掉用者提供一個比較函數,這樣排序函數藉此調用調用者的函數來比較大小。
應用詳解:
回調在C語言中是通過函數指標來實現的,通過將回呼函數的地址傳給被掉函數從而實現回調。因此要實現回調,必須首先定義函數指標,如:
void Func(char *s);函數原型
void (*pFunc)(char *);//函數指標
可以看出,函數的定義和函數指標的定義非常類似,一般來說,為了簡化函數指標類型變數定義,提高程式的可讀性,我們需要把函數指標類型自訂一下。
typedef void(*pcb)(char *) pcb;
被調函數的例子:
void GetCallBack(pcb callBack)
{
/*do something*/
}
使用者在調用上面的函數時,需要自己實現一個pcb類型的回呼函數:
void fCallBack(char *s)
{
/*do something*/
}
然後,就可以直接把fCallBack當作一個變數傳遞給GetCallBack,
GetCallBack(fCallBack)
如果賦了不同的值給該參數,那麼調用者將調用不同地址的函數。賦值可以發生在運行時,這樣使你能實現動態綁定。
(2 )參數路由規則
到目前為止,我們只討論了函數指標及回調而沒有去注意ANSI C/C++的編譯器規範。許多編譯器有幾種調用規範。如在Visual C++中,可以在函數類型前加_cdecl,_stdcall或者_pascal來表示其調用規範(預設為_cdecl)。C++ Builder也支援_fastcall調用規範。調用規範影響編譯器產生的給定函數名,參數傳遞的順序(從右至左或從左至右),堆棧清理責任(調用者或 者被調用者)以及參數傳遞機制(堆棧,CPU寄存器等)。
將調用規範看成是函數類型的一部分是很重要的;不能用不相容的調用規範將地址賦值給函數指標。例如:
// 被調用函數是以int為參數,以int為傳回值
__stdcall int callee(int);
// 調用函數以函數指標為參數
void caller( __cdecl int(*ptr)(int));
// 在p中企圖儲存被調用函數地址的非法操作
__cdecl int(*p)(int) = callee; // 出錯
指標p和callee()的類型不相容,因為它們有不同的調用規範。因此不能將被調用者的地址賦值給指標p,儘管兩者有相同的傳回值和參數列
(3 )應用舉例
C語言的標準庫函數中很多地方就採用了回呼函數來讓使用者定製處理過程。如常用的快速排序函數、二分搜尋函數等。
快速排序函數原型:
void qsort(void *base, size_t nelem, size_t width, int (_USERENTRY *fcmp)(const void *, const void *));
二分搜尋函數原型:
void *bsearch(const void *key, const void *base, size_t nelem,
size_t width, int (_USERENTRY *fcmp)(const void *, const void *));
其中fcmp就是一個回呼函數的變數。
下面給出一個具體的例子:
#include <stdio.h>
#include <stdlib.h>
int sort_function( const void *a, const void *b);
int list[5] = { 54, 21, 11, 67, 22 };
int main(void)
{
int x;
qsort((void *)list, 5, sizeof(list[0]), sort_function);
for (x = 0; x < 5; x++)
printf("%i\n", list[x]);
return 0;
}
int sort_function( const void *a, const void *b)
{
return *(int*)a-*(int*)b;
}