在層次化程序設計中,上層模塊可以直接調用下層模塊的函數,而下層模塊一般不能直接調用上層模塊的函數。而實際情況中卻常常存在層間相互依賴的情況,即層間相互調用函數,例如,層B的狀態變化需要通知層A或者引起層B的狀態變化,為了避免這種相互依賴,可以使用回調函數。假設層A位於層B的上層,層A調用層B的函數,稱層A為caller,層B中被調用的函數被稱為callee,層A中被callee回調的函數稱為callbacker。
1. 回調函數
回調函數是通過caller向callee傳遞callbacker的函數指針實現,當在callee中callbacker被調用時,稱為發生回調,而callbacker則稱為回調函數。callee無需關心callbacker的實現細節和所處理的具體的數據類型,僅需知道callbacker的原型即可,而callbacker的實現由caller負責,其中包括實現細節(算法)和數據類型。
回調函數可以實現動態綁定,即通過在運行時向callee傳遞不同的函數指針,從而調用不同的函數。例如,排序算法中需要按某種規則比較數據,callee無需知道數據比較的方法以及數據的類型,而僅僅關心比較數據的個數以及比較結果的含義,具體的比較操作由callbacker負責,數據類型可以是原始數據類型也可以是結構體類型。
回調可以實現消息通知和事件驅動,比如callee中發生某個事件時,需要通知caller或者需要caller完成某種功能,則可以通過回調機制實現。
2. 函數指針
回調機制是通過傳遞函數指針實現,而函數指針則是指向函數的指針,函數指針的定義可以使用兩種形式:
(1)直接定義
函數返回類型 (*函數指針名)(形參表);
(2)使用typedef
typedef 函數返回類型 (*新類型名)(形參表);
新類型名 函數指針名;
int add_int(int a, int b){return a+b;}
int (*pfunc)(int x, int y);
pfun = add_int;//或者&add_int
typedef int (*PFUNC)(int x, int y);
PFUNC pfunx;
pfunx = &add_int;
3. 返回函數指針的函數
即函數的返回值是一個函數指針,也可以有兩種定義形式:
(1)直接定義
函數返回類型 (*函數名(形參表1))(形參表2){......}
定義了一個名稱由“函數名”標識的函數,其參數由“形參表1”標識,該函數返回一個函數指針,指向一個由“函數返回類型”標識返回類型、參數個數以及類型符合“形參表2”的函數。
(2)使用typedef
typedef 函數返回類型 (*新類型名)(形參表2);
新類型名 函數名(形參表1){......}
實例:
void (*signal(int sig, void (*func)(int)))(int);
函數名:signal
功能:指定處理信號的函數,sig指定信號,func為處理該信號的函數,具有一個整型的參數
返回值:為一個函數指針,指向一個具有一個整型參數、返回值類型為void的函數,該函數參數類型以及返回值類型與func函數一致;即返回該信號之前的處理函數
#include <signal.h>
char tmpfilename [L_tmpnam];
void terminate (int param)
{
printf ("Terminating program...\n");
remove (tmpfilename);//移除臨時文件
exit(1);
}
int main(void)
{
void (*prev_fn)(int);//定義函數指針
FILE *fp;
prev_fn = signal (SIGTERM,terminate);//為SIGTERM信號指定處理函數terminate
tmpnam (tmpfilename);//生成臨時文件名並保存在tmpfilename中
fp = fopen(tmpfilename, "w+");
fprintf(fp, "test");
fclose(fp);
raise(SIGTERM);//生成SIGTERM信號並通知進程
if (prev_fn==SIG_IGN) signal (SIGTERM,SIG_IGN);//重新設定默認處理函數
return 0;
}
4. 函數指針數組
指向一組有相同返回類型以及參數個數和順序的函數,常用來替換switch/if結構,也可以有兩種定義形式:
(1)直接定義
函數返回類型 (*函數指針數組名[N])(形參表);
(2)使用typedef
typedef 函數返回類型 (*新類型名)(形參表);
新類型名 函數指針數組名[N];