Linux C 函式指標應用---回撥函式
(這裡引用了知乎上一些知友的回答,感覺不錯,有助於理解,這裡引用作為借鑑,如有冒犯,煩請告知)
我們先來回顧一下函式指標,函式指標是專門用來存放函式地址的指標,函式地址是一個函式的入口地址,函式名代表了函式的入口地址。當一個函式指標指向了一個函式,就可以通過這個指標來呼叫該函式,可以將函式作為引數傳遞給函式指標。
那函式指標在我們實際程式設計中會起到怎樣的作用呢?前一篇關於函式指標的文章中,我們已經提到執行緒建立、訊號註冊中都用到了函式指標,下面我們將介紹函式指標的一個經典應用---回撥函式。
看一看百度百科如何定義的回撥函式:
回撥函式就是一個通過函式指標呼叫的函式。如果你把函式的指標(地址)作為引數傳遞給另一個函式,當這個指標被用來呼叫其所指向的函式時,我們就說這是回撥函式。回撥函式不是由該函式的實現方直接呼叫,而是在特定的事件或條件發生時由另外的一方呼叫的,用於對該事件或條件進行響應。
簡單的講,一般寫程式是你呼叫系統的API,如果把關係反過來,你寫一個函式,讓系統呼叫你的函式,那就是回調了,那個被系統呼叫的函式就是回撥函式。
說詳細點,我們知道,程式設計分為兩類:系統程式設計(system programming)和應用程式設計(application programming)。所謂系統程式設計,簡單來說,就是編寫庫;而應用程式設計就是利用寫好的各種庫來編寫具某種功用的程式,也就是應用。系統程式設計師會給自己寫的庫留下一些介面,即API(application programming interface,應用程式設計介面),以供應用程式設計師使用。所以在抽象層的圖示裡,庫位於應用的底下。
當程式跑起來時,一般情況下,應用程式(application program)會時常通過API呼叫庫裡所預先備好的函式。但是有些庫函式(library function)卻要求應用先傳給它一個函式,好在合適的時候呼叫,以完成目標任務。這個被傳入的、後又被呼叫的函式就稱為回撥函式(callback function)。
我們可以這樣理解:有一家旅館提供叫醒服務,但是要求旅客自己決定叫醒的方法。可以是打客房電話,也可以是派服務員去敲門,睡得死怕耽誤事的,還可以要求往自己頭上澆盆水。這裡,“叫醒”這個行為是旅館提供的,相當於庫函式,但是叫醒的方式是由旅客決定並告訴旅館的,也就是回撥函式。而旅客告訴旅館怎麼叫醒自己的動作,也就是把回撥函式傳入庫函式的動作,稱為登記回撥函式
這樣看是不是都暈了,沒事,我們再來一次總結:
回撥函式通俗的解釋:
普通函式:你所寫的函式呼叫系統函式,你只管呼叫,不管實現。
回撥函式:系統呼叫你所寫的函式,你只管實現,不管呼叫。
那回調函式到底是如何使用的呢?我們先來解決個小問題:
1、回撥函式在什麼場景有用?
我要在特定時候執行一個任務,至於是什麼時候我自己都不知道。比如某一時間到了或者某一事件發生或者某一中斷觸發。
2、回撥函式怎麼起作用?
把我要執行的這個任務寫成一個函式,將這個函式和某一時間或者事件或者中斷建立關聯。當這個關聯完成的時候,這個函式華麗的從普通函式變身成為回撥函式。
3、回撥函式什麼時候執行?
當該回撥函式關心的那個時間或者事件或者中斷觸發的時候,回撥函式將被執行。
一般是觸發這個時間、事件或中斷的程式主體(通常是個函式或者物件)觀察到有一個關注這個東東的回撥函式的時候,這個主體負責呼叫這個回撥函式。
4、回撥函式有什麼好處?
最大的好處是你的程式變成非同步了。也就是你不必再呼叫這個函式的時候一直等待這個時間的到達、事件的發生或中斷的發生(萬一一直不發生,你的程式會怎麼樣?)。再此期間你可以做做別的事情,或者四處逛逛。當回撥函式被執行時,你的程式重新得到執行的機會,此時你可以繼續做必要的事情了。
借鑑知友的一個例子:
你去食堂打飯,你喜歡吃小炒熱飯菜,所以你去了一個小炒視窗。
你跟老闆說了要×××蓋飯,老闆說:你是100號,喊到你的號你就來拿菜。
然後你在旁邊跟同學吹牛、或者看手機、或者乾點你想幹的任何事情。。。
然後你聽到老闆喊100號並且把菜放到視窗,你走到視窗,拿到你的菜。
這裡面有幾個函式:
老闆的部分:
1、老闆提供一個點餐的函式 boss.Order(string 菜名,double 錢)
2、老闆有個做飯的函式,此函式耗時較長boss.Cook()
3、老闆提供一個事件,當boss.cook()執行完時,該事件被觸發,boss.OnCookFinish;
你的部分:
1、你需要有一個函式去訂餐,也就是你的函式中需要執行類似於boss.Order("紅燒肉蓋澆飯",20),比如是me.Hungry()
2、你需要有一個函式作為回撥函式去關注boss.OnCookFinish事件,這樣當老闆做好飯,你就可以知道是不是你的好了。
由於老闆的事件發生的時候中會喊編號並且吧菜放到視窗,所以你的回撥函式需要能夠接受1個編號和1個菜作為引數。
比如me.AcceptFood(int currNumber,object food)
所以整個程式的流程其實是這樣的:
me.Hungry()
{
boss.Order("紅燒肉蓋澆飯",20);
boss.OnCookFinish+=me.AcceptFood;//此處表面,AcceptFood這個回撥函式關心OnCookFinish事件,並且變成這個事件的回撥函式
//此時這個函式執行完,不再等待
}
boss.Order("紅燒肉蓋澆飯",20)
{
//收錢
//配菜 前2個耗時較短
boss.Cook();//此處一般會開新執行緒執行cook動作
}
boss.Cook()
{
//cooking~~~~~~~~~~
//完成了,下面將要觸發事件,系統將檢查這個事件是否有回撥函式關心,有的話逐個回撥。
OnCookFinish(100號,紅燒肉蓋澆飯);
}
至此案例基本完成了一個完整的任務流程。
終於到我們的例項環節了!
回撥函式例項一:
#include<stdio.h>
//函式指標的格式為:int (*ptr)(char *p) 即:返回值(指標名)(引數列表)
typedef int (*CallBackFun)(char *p); //為回撥函式命名,型別命名為CallBackFun,引數為char *p
//函式 Afun,格式符合 CallBackFun 的格式,因此可以看作是一個 CallBackFun
int Afun(char *p)
{
printf("Afun 回撥打印出字元%s!\n", p);
return 0;
}
//函式Cfun,格式符合 CallBackFun 的格式,因此可以看作是一個CallBackFun
int Cfun(char *p)
{
printf("Cfun 回撥列印:%s, Nice to meet you!\n", p);
return 0;
}
//執行回撥函式,方式一:通過命名方式,pCallBack可以看做是CallBackFun的別名
int call(CallBackFun pCallBack, char *p)
{
printf("call 直接打印出字元%s!\n", p);
pCallBack(p);
return 0;
}
// 執行回撥函式,方式二:直接通過方法指標
int call2(char *p, int (*ptr)()) //或者是int call2(char *p, int (*ptr)(char *))同時ptr可以任意取名
{
printf("======================================\n");
(*ptr)(p);
}
int main()
{
char *p = "hello";
call(Afun, p);
call(Cfun, p);
call2(p, Afun);
call2(p, Cfun);
return 0;
}
執行結果如下:
fs@ubuntu:~/qiang/huidiao$ ./huidiao1
call 直接打印出字元hello!
Afun 回撥打印出字元hello!
call 直接打印出字元hello!
Cfun 回撥列印:hello, Nice to meet you!
======================================
Afun 回撥打印出字元hello!
======================================
Cfun 回撥列印:hello, Nice to meet you!
fs@ubuntu:~/qiang/huidiao$ ./huidiao1
回撥函式應用例項二:
#include <stdio.h>
typedef void (*callback)(char *);
void repeat(callback function, char *para)
{
function(para);
function(para);
}
void hello(char* a)
{
printf("Hello %s\n",(const char *)a);
}
void count(char *num)
{
int i;
for(i = 1;i < (int)num;i++)
printf("%d",i);
putchar('\n');
}
int main(void)
{
repeat(hello,"xiaoqiang");
repeat(count, (char *)4);
}
執行結果如下:
fs@ubuntu:~/qiang/huidiao$ ./huidiao2
Hello xiaoqiang
Hello xiaoqiang
123
123
fs@ubuntu:~/qiang/huidiao$
希望對大家有幫助,謝謝!