亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C > 正文

詳解C語言編程中的函數指針以及函數回調

2020-01-26 14:38:19
字體:
來源:轉載
供稿:網友

函數指針:

就是存儲函數地址的指針,就是指向函數的指針,就是指針存儲的值是函數地址,我們可以通過指針可以調用函數。

我們先來定義一個簡單的函數:

//定義這樣一個函數void easyFunc(){  printf("I'm a easy Function/n");}//聲明一個函數void easyFunc();//調用函數easyFunc();//定義這樣一個函數void easyFunc(){  printf("I'm a easy Function/n");}//聲明一個函數void easyFunc();//調用函數easyFunc();

上面三個步驟就是我們在學習函數的時候必須要做的,只有通過以上三步我們才算定義了一個完整的函數。

如何定義一個函數指針呢?前面我們定義其他類型的指針的格式是 類型 * 指針名 = 一個地址,比如:

int *p = &a;//定義了一個存儲整形地址的指針p

也就是說如果我們要定義什么類型的指針就得知道什么類型,那么函數的類型怎么確定呢?函數的類型就是函數的聲明把函數名去掉即可,比如上面的函數的類型就是:

void ()

我們再來聲明一個有參數和返回值的函數:

int add(int a, int b);

上面函數的類型依舊是把函數名去掉即可:

int (int a, int b)

既然我們知道了函數的類型那么函數指針的類型就是在后面加個 * 即可,是不是這樣呢?

int (int a, int b) * //這個是絕對錯誤的

上面這么定義是錯誤的,絕對是錯誤的,很多初學者都這樣去做,總覺得就應該這樣,其實函數指針的類型的定義正好比較特殊,它是這樣的:

int (*) (int a, int b);//這里的型號在中間,一定要用括號括起來int (*) (int a, int b);//這里的型號在中間,一定要用括號括起來

我們定義函數指針只需在 * 后面加個指針名稱即可,也就是下面這樣:

int (*p)(int a, int b) = NULL;//初始化為 NULLint (*p)(int a, int b) = NULL;//初始化為 NULL

如果我們要給 p 賦值的話,我們就應該定義一個返回值類型為 int ,兩個參數為 int 的函數:

int add(int a, int b){  return a + b;}p = add;//給函數指針賦值int add(int a, int b){  return a + b;}p = add;//給函數指針賦值

經過上面的賦值,我們就可以使用 p 來代表函數:

p(5, 6);//等價于 add(5, 6);printf("%d/n", p(5, b));p(5, 6);//等價于 add(5, 6);printf("%d/n", p(5, b));

輸出結果為:11

通過上面的指針函數來使用函數,一般不是函數的主要用法,我們使用函數指針主要是用來實現函數的回調,通過把函數作為參數來使用。

函數指針的值

函數指針跟普通指針一樣,存的也是一個內存地址, 只是這個地址是一個函數的起始地址, 下面這個程序打印出一個函數指針的值(func1.c):

#include <stdio.h>typedef int (*Func)(int);int Double(int a){  return (a + a);}int main(){  Func p = Double;  printf("%p/n", p);  return 0;}

編譯、運行程序:

[lqy@localhost notlong]$ gcc -O2 -o func1 func1.c[lqy@localhost notlong]$ ./func10x80483d0[lqy@localhost notlong]$ 

然后我們用 nm 工具查看一下 Double 的地址, 看是不是正好是 0x80483d0:

[lqy@localhost notlong]$ nm func1 | sort08048294 T _init08048310 T _start08048340 t __do_global_dtors_aux080483a0 t frame_dummy080483d0 T Double080483e0 T main...

  不出意料,Double 的起始地址果然是 0x080483d0。

函數回調

函數回調的本質就是讓函數指針作為函數參數,函數調用時傳入函數地址,也就是函數名即可。

我們什么時候使用回調函數呢?咱們先舉個例子,比如現在小明現在作業有個題不會做,于是給小紅打電話說:我現在作業有個題不會做,你能幫我做下嗎?然后把答案告訴我?小紅聽到后覺得這個題也不是立刻能做出來的,所以跟小明說我做完之后告訴你。這個做完之后告訴小明就是函數的回調,如何告訴小明,小紅必須有小明的聯系方式,這個聯系方式就是回調函數。接下來我們用代碼來實現:

小明需要把聯系方式留給小紅,而且還得得到答案,因此需要個參數來保存答案:

void contactMethod(int answer){  //把答案輸出  printf("答案為:%d/n", answer);}void contactMethod(int answer){  //把答案輸出  printf("答案為:%d/n", answer);}小紅這邊得拿到小明的聯系方式,需要用函數指針來存儲這個方法:void tellXiaoMing(int xiaoHongAnswer, void (*p)(int)){  p(xiaoHongAnswer);}//當小紅把答案做出來的時候,小紅把答案通過小明留下的聯系方式傳過去tellXiaoMing(4, contactMethod);void tellXiaoMing(int xiaoHongAnswer, void (*p)(int)){  p(xiaoHongAnswer);}//當小紅把答案做出來的時候,小紅把答案通過小明留下的聯系方式傳過去tellXiaoMing(4, contactMethod);


上面的回調有人會問為什么我們不能直接 tellXiaoMing 方法中直接調用 contactMethod 函數呢?因為小紅如果用函數指針作為參數的時候,不僅可以存儲小明的聯系方式,還可以存儲小軍的聯系方式,這樣的話我這邊的代碼就不用修改了,你只需要傳入不同的參數就行了,因此這樣的設計代碼重用性很高,靈活性很大。

函數回調的整個過程就是上面這樣,這里有個主要特點就是當我們使用回調的時候,一般用在一個方法需要等待操作的時候,比如上面的小紅要等到答案做出來的時候才通知小明,不如當小明問小紅時,小紅直接能給出答案,就沒必要有回調了,那執行順序就是:
2016420152222457.png (458×514)
回調的順序是:

2016420152256870.jpg (904×850)

上面的小紅做題是個等待操作,比較耗時,小明也不能一直拿著電話等待,所以只有小紅做出來之后,再把電話打回去才能告訴小明答案。

因此函數回調有兩個主要特征:

函數指針作為參數,可以傳入不同的函數,因此可以回調不同的函數
函數回調一般使用在需要等待或者耗時操作,或者得在一定時間或者事件觸發后回調執行的情況下
我們使用函數回調來實現一個動態排序,我們現在個學生的結構體,里面包含了姓名,年齡,成績,我們有個排序學生的方法,但是具體是按照姓名排?還是年齡排?還是成績排?這個是不確定的,或者一會還會有新需求,因此通過動態排序寫好之后,我們只需傳入不同的函數即可。

定義學生結構體:

//定義個結構體 student,包含name,age 和 scorestruct student {  char name[255];  int age;  float score;};//typedef struct student 為 Studenttypedef struct student Student;

定義比較結果的枚舉:

//定義比較結果枚舉enum CompareResult {  Student_Lager = 1, //1 代表大于  Student_Same = 0,// 0 代表等于  Student_Smaller = -1// -1 代表小于};//typedef enum CompareResult 為 StudentCompareResulttypedef enum CompareResult StudentCompareResult;

定義成績,年齡和成績比較函數:

/*  通過成績來比較學生*/StudentCompareResult compareByScore(Student st1, Student st2){  if (st1.score > st2.score) {//如果前面學生成績高于后面學生成績,返回 1    return Student_Lager;  }  else if (st1.score == st2.score) {//如果前面學生成績等于后面學生成績,返回 0    return Student_Same;  }  else { //如果前面學生成績低于后面學生成績,返回 -1    return Student_Smaller;  }} /*  通過年齡來比較學生*/StudentCompareResult compareByAge(Student st1, Student st2){  if (st1.age > st2.age) {//如果前面學生年齡大于后面學生年齡,返回 1    return Student_Lager;  }  else if (st1.age == st2.age) {//如果前面學生年齡等于后面學生年齡,返回 0   return Student_Same;  }  else {//如果前面學生年齡小于后面學生年齡,返回 -1    return Student_Smaller;  } } /*   通過名字來比較學生*/StudentCompareResult compareByName(Student st1, Student st2){  if (strcmp(st1.name, st2.name) > 0) {//如果前面學生名字在字典中的排序大于后面學生名字在字典中的排序,返回 1    return Student_Lager;  }  else if (strcmp(st1.name, st2.name) == 0) {//如果前面學生名字在字典中的排序等于后面學生名字在字典中的排序,返回 0    return Student_Same;  }  else {//如果前面學生名字在字典中的排序小于后面學生名字在字典中的排序,返回 -1    return Student_Smaller;  }  }

定義排序函數:

/*  根據不同的比較方式進行學生排序  stu1[]:學生數組  count :學生個數  p :函數指針,來傳遞不同的比較方式函數*/void sortStudent(Student stu[], int count, StudentCompareResult (*p)(Student st1, Student st2)){  for (int i = 0; i < count - 1; i++) {    for (int j = 0; j < count - i - 1; j++) {      if (p(stu[j], stu[j + 1]) > 0) {        Student tempStu = stu[j];     stu[j] = stu[j + 1];       stu[j + 1] = tempStu;      }    }  }}

定義結構體數組:

//定義四個學生結構體Student st1 = {"lingxi", 24, 60.0};Student st2 = {"blogs", 25, 70.0};Student st3 = {"hello", 15, 100};Student st4 = {"world", 45, 40.0};//定義一個結構體數組,存放上面四個學生Student sts[4] = {st1, st2, st3, st4};

輸出排序前的數組,排序和排序后的數組:

//輸出排序前數組中的學生名字printf("排序前/n");for (int i = 0; i < 4; i++) {  printf("name = %s/n", sts[i].name);//輸出名字}//進行排序sortStudent(sts, 4, compareByName);//輸出排序后數組中的學生名字printf("排序后/n");for (int i = 0; i < 4; i++) {  printf("name = %s/n", sts[i].name);}

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
91精品国产九九九久久久亚洲| 91在线国产电影| 久久久91精品国产| 亚洲美女又黄又爽在线观看| 国产成人一区二区在线| 欧美国产日韩二区| 国产精品av在线播放| 中文字幕一区二区精品| 91国产视频在线| 欧美黑人性生活视频| 亚洲国产精彩中文乱码av| 姬川优奈aav一区二区| 亚洲国产高清高潮精品美女| 精品视频在线播放| 欧美大片在线看免费观看| 孩xxxx性bbbb欧美| 日本精品免费观看| 国产91在线高潮白浆在线观看| 国产精品扒开腿做爽爽爽视频| 91精品综合视频| 亚洲大胆人体在线| 国产精品扒开腿爽爽爽视频| 久久99久久99精品免观看粉嫩| 精品视频久久久| 国产亚洲欧洲高清| 国产原创欧美精品| 亚洲aaa激情| 国产精品美女免费视频| 日韩在线精品一区| 日韩激情第一页| 国产国语videosex另类| 亚洲日本成人女熟在线观看| 91理论片午午论夜理片久久| www.日韩欧美| 亚洲人成在线播放| 国产精品视频大全| 国产精品久久久久久久久影视| 国内精品模特av私拍在线观看| 亚洲国产另类久久精品| 久久亚洲一区二区三区四区五区高| 精品国产区一区二区三区在线观看| 欧美精品激情视频| 精品一区二区三区三区| 成人av在线亚洲| 国产69久久精品成人| 亚洲男人天堂九九视频| 曰本色欧美视频在线| 国产一区二区三区在线观看视频| 国产成人精品综合| 少妇高潮久久久久久潘金莲| 亚洲精选一区二区| 国产精品九九九| 91精品国产自产在线观看永久| 综合国产在线视频| 国产精品视频免费在线| 欧美激情亚洲综合一区| 亚洲毛片在线观看| 欧美人成在线视频| 日本不卡视频在线播放| 日本久久久久久| 欧美影院成年免费版| 亚洲精品久久久久久久久久久久久| 精品电影在线观看| 亚洲老头老太hd| 国内外成人免费激情在线视频网站| 亚洲永久在线观看| 国产精品亚洲欧美导航| 国产成人一区二区三区| 97碰在线观看| 亚洲一区二区三区视频| 色噜噜亚洲精品中文字幕| 欧洲成人免费aa| 精品亚洲一区二区三区在线播放| 国产在线999| 国产小视频国产精品| 国产精品久久久久久久7电影| 欧美激情第一页xxx| 午夜精品三级视频福利| 亚洲精品国产精品久久清纯直播| 亚洲2020天天堂在线观看| 亚洲图片欧美日产| 欧美第一黄网免费网站| 日韩精品极品在线观看播放免费视频| 成人激情春色网| 国产激情视频一区| 欧美综合激情网| 国产精品6699| 国产亚洲精品高潮| 日本久久久久久久久久久| 国产成人一区二区三区小说| 亚洲网址你懂得| 国产欧美婷婷中文| 亚洲人成在线观看网站高清| 亚洲男人天堂手机在线| 欧美成人午夜激情视频| 草民午夜欧美限制a级福利片| 精品国产依人香蕉在线精品| 91免费版网站入口| 91精品国产91久久久久久最新| 国产精品高清在线| 欧美激情一二区| 中文欧美日本在线资源| 国产精自产拍久久久久久| 久久久精品国产网站| 亚洲男人的天堂网站| 亚洲精品福利在线| 久久久精品免费视频| 国产精品久久久久久久美男| 久久亚洲私人国产精品va| 国产精品美女www爽爽爽视频| 成人动漫网站在线观看| 久久在线免费观看视频| 亚洲精品福利在线观看| 日韩中文字幕免费看| 国产午夜精品视频免费不卡69堂| 国产在线不卡精品| www.日韩欧美| 精品呦交小u女在线| 国产在线视频91| 午夜精品免费视频| 久久久久久久国产精品视频| 日韩欧中文字幕| 成人乱色短篇合集| 日韩最新中文字幕电影免费看| 国产99久久精品一区二区永久免费| 亚洲午夜性刺激影院| 国产一区av在线| 国产精品久久久久久av福利软件| 91国偷自产一区二区三区的观看方式| 欧美专区福利在线| 欧美性受xxxx白人性爽| 4438全国亚洲精品在线观看视频| 日韩av手机在线观看| 91丝袜美腿美女视频网站| 亚洲欧美国产日韩天堂区| 成人天堂噜噜噜| 91香蕉电影院| 欧美精品免费看| 久久久之久亚州精品露出| 久热爱精品视频线路一| 欧美丝袜美女中出在线| 欧美亚洲午夜视频在线观看| 高清在线视频日韩欧美| 久久久久一本一区二区青青蜜月| 亚洲视频精品在线| 一区二区三区www| 国产手机视频精品| 在线午夜精品自拍| 亚洲女人天堂色在线7777| 成人午夜一级二级三级| 欧美一级在线亚洲天堂| 大胆人体色综合| 欧美日韩在线第一页| 96精品久久久久中文字幕| 国产精品日日做人人爱| 亚洲一区二区三区在线视频| 国产精品一区专区欧美日韩| 成人久久久久久| 亚洲精品二三区| 亚洲人高潮女人毛茸茸| 亚洲区中文字幕| 亚洲最大av网| 久久久综合免费视频| 国产成人精品一区二区三区|