日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux C 函数指针应用---回调函数

發布時間:2023/12/9 linux 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux C 函数指针应用---回调函数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?(這里引用了知乎上一些知友的回答,感覺不錯,有助于理解,這里引用作為借鑒,如有冒犯,煩請告知)

??? 我們先來回顧一下函數指針,函數指針是專門用來存放函數地址的指針,函數地址是一個函數的入口地址,函數名代表了函數的入口地址。當一個函數指針指向了一個函數,就可以通過這個指針來調用該函數,可以將函數作為參數傳遞給函數指針。

??? 那函數指針在我們實際編程中會起到怎樣的作用呢?前一篇關于函數指針的文章中,我們已經提到線程創建、信號注冊中都用到了函數指針,下面我們將介紹函數指針的一個經典應用---回調函數。

??? 看一看百度百科如何定義的回調函數:

?? ?回調函數就是一個通過函數指針調用的函數。如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用于對該事件或條件進行響應。

???? 簡單的講,一般寫程序是你調用系統的API,如果把關系反過來,你寫一個函數,讓系統調用你的函數,那就是回調了,那個被系統調用的函數就是回調函數

???? 說詳細點,我們知道,編程分為兩類:系統編程(system programming)和應用編程(application programming)。所謂系統編程,簡單來說,就是編寫;而應用編程就是利用寫好的各種庫來編寫具某種功用的程序,也就是應用。系統程序員會給自己寫的庫留下一些接口,即API(application programming interface,應用編程接口),以供應用程序員使用。所以在抽象層的圖示里,庫位于應用的底下。

???? 當程序跑起來時,一般情況下,應用程序(application program)會時常通過API調用庫里所預先備好的函數。但是有些庫函數(library function)卻要求應用先傳給它一個函數,好在合適的時候調用,以完成目標任務。這個被傳入的、后又被調用的函數就稱為回調函數(callback function)。

???? 我們可以這樣理解:有一家旅館提供叫醒服務,但是要求旅客自己決定叫醒的方法。可以是打客房電話,也可以是派服務員去敲門,睡得死怕耽誤事的,還可以要求往自己頭上澆盆水。這里,“叫醒”這個行為是旅館提供的,相當于庫函數,但是叫醒的方式是由旅客決定并告訴旅館的,也就是回調函數。而旅客告訴旅館怎么叫醒自己的動作,也就是把回調函數傳入庫函數的動作,稱為登記回調函數(to register a 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)

所以整個程序的流程其實是這樣的:

[cpp]?view plaincopy
  • me.Hungry()??
  • {??
  • ????boss.Order("紅燒肉蓋澆飯",20);??
  • ????boss.OnCookFinish+=me.AcceptFood;//此處表面,AcceptFood這個回調函數關心OnCookFinish事件,并且變成這個事件的回調函數??
  • ????//此時這個函數執行完,不再等待??
  • }??
  • ??
  • boss.Order("紅燒肉蓋澆飯",20)??
  • {??
  • ????//收錢??
  • ????//配菜?前2個耗時較短??
  • ????boss.Cook();//此處一般會開新線程執行cook動作??
  • }??
  • ??
  • boss.Cook()??
  • {??
  • ????//cooking~~~~~~~~~~??
  • ????//完成了,下面將要觸發事件,系統將檢查這個事件是否有回調函數關心,有的話逐個回調。??
  • ????OnCookFinish(100號,紅燒肉蓋澆飯);??
  • }??
  • 至此案例基本完成了一個完整的任務流程。

    終于到我們的實例環節了!
    回調函數實例一:

    [cpp]?view plaincopy
  • #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;??
  • }??
  • 執行結果如下:

    [cpp]?view plaincopy
  • 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??
  • ?

    回調函數應用實例二:

    [cpp]?view plaincopy
  • #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);??
  • }??
  • 執行結果如下:

    [cpp]?view plaincopy
  • fs@ubuntu:~/qiang/huidiao$?./huidiao2??
  • Hello?xiaoqiang??
  • Hello?xiaoqiang??
  • 123??
  • 123??
  • fs@ubuntu:~/qiang/huidiao$???
  • ?

    希望對大家有幫助,謝謝!

    創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

    總結

    以上是生活随笔為你收集整理的Linux C 函数指针应用---回调函数的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。