點擊上方“C語言與CPP編程”,選擇“關注/置頂/星標公眾號”
干貨福利,第一時間送達!
最近有小伙伴說沒有收到當天的文章推送,這是因為微信更改了推送機制,導致沒有星標公眾號的小伙伴刷不到當天推送的文章,無法接收到一些比較實用的知識和資訊。所以建議大家加個星標??,以后就能第一時間收到推送了。
原文:
https://blog.csdn.net/qq_41854911/article/details/121058935
int?*p1;
char?*p2;
STRUCT?*p3;?// STRUCT為我們定義的結構體下面我們來了解一下函數指針的概念和使用方法。
1. 概念
函數返回值類型?? (* 指針變量名) (函數參數列表);
一般為了方便使用,我們會選擇:
typedef? 函數返回值類型? (* 指針變量名) (函數參數列表);
比如:
typedef?int?(*Fun1)(int);?//聲明也可寫成int (*Fun1)(int x),但習慣上一般不這樣。
typedef?int?(*Fun2)(int,?int);?//參數為兩個整型,返回值為整型
typedef?void?(*Fun3)(void);?//無參數和返回值
typedef?void*?(*Fun4)(void*);?//參數和返回值都為void*指針2. 如何用函數指針調用函數
int?Func(int?x);???/*聲明一個函數*/
int?(*p)?(int?x);??/*定義一個函數指針*/
p?=?Func;??????????/*將Func函數的首地址賦給指針變量p*/
p?=?&Func;?????????/*將Func函數的首地址賦給指針變量p*/賦值時函數 Func 不帶括號,也不帶參數。由于函數名 Func 代表函數的首地址,因此經過賦值以后,指針變量 p 就指向函數 Func() 代碼的首地址了。
#include?
int?Max(int,?int);??//函數聲明
int?main(void)
{
????int(*p)(int,?int);??//定義一個函數指針
????int?a,?b,?c;
????p?=?Max;??//把函數Max賦給指針變量p,?使p指向Max函數
????printf("please?enter?a?and?b:");
????scanf("%d%d",?&a,?&b);
????c?=?(*p)(a,?b);??//通過函數指針調用Max函數
????printf("a?=?%d\nb?=?%d\nmax?=?%d\n",?a,?b,?c);
????return?0;
}
int?Max(int?x,?int?y)??//定義Max函數
{
????int?z;
????if?(x?>?y)
????{
????????z?=?x;
????}
????else
????{
????????z?=?y;
????}
????return?z;
}特別注意的是,因為函數名本身就可以表示該函數地址(指針),因此在獲取函數指針時,可以直接用函數名,也可以取函數的地址。
p?=?Max? 可以改成? p?=?&Max
c?=?(*p)(a,?b)? 可以改成? c?=?p(a,?b)3. 函數指針作為某個函數的參數
#include?
#include?
//前加一個typedef關鍵字,這樣就定義一個名為FunType函數指針類型,而不是一個FunType變量。
//形式同?typedef?int*?PINT;typedef?void(*FunType)(int);
void?myFun(int?x);
void?hisFun(int?x);
void?herFun(int?x);
void?callFun(FunType?fp,int?x);
int?main()
{
????callFun(myFun,100);//傳入函數指針常量,作為回調函數
????callFun(hisFun,200);
????callFun(herFun,300);
????return?0;
}
void?callFun(FunType?fp,int?x)
{
????fp(x);//通過fp的指針執行傳遞進來的函數,注意fp所指的函數有一個參數
}
void?myFun(int?x)
{
????printf("myFun:?%d\n",x);
}
void?hisFun(int?x)
{
????printf("hisFun:?%d\n",x);
}
void?herFun(int?x)
{
????printf("herFun:?%d\n",x);
}

4. 函數指針作為函數返回類型
有了上面的基礎,要寫出返回類型為函數指針的函數應該不難了,下面這個例子就是返回類型為函數指針的函數:
void?(*?func5(int,?int,?float))(int,?int)
{
????...
}func5?以?(int, int, float)?為參數,其返回類型為?void (\*)(int, int)?。在C語言中,變量或者函數的聲明也是一個大學問,想要了解更多關于聲明的話題,可以參考《C專家編程》(1-3章)。這本書的第三章花了整整一章的內容來講解如何讀懂C語言的聲明。在開始講解回調函數前,最后介紹一下函數指針數組。既然函數指針也是指針,那我們就可以用數組來存放函數指針。下面我們看一個函數指針數組的例子:
/*?方法 1?*/
void?(*func_array_1[5])(int,?int,?float);
/*?方法 2?*/
typedef?void?(*p_func_array)(int,?int,?float);
p_func_array?func_array_2[5];void (\*)(int, int, float)? *?的函數指針數組。
函數指針常量 :Max;函數指針變量:p;
數名調用如果都得如 (*myFun)(10) 這樣,那書寫與讀起來都是不方便和不習慣的。所以C語言的設計者們才會設計成又可允許 myFun(10) 這種形式地調用(這樣方便多了,并與數學中的函數形式一樣)。
在函數指針變量也可以存入一個數組內。數組的聲明方法:int (*fArray[10]) (int);
我們先來看看百度百科是如何定義回調函數的:
回調函數就是一個通過函數指針調用的函數。如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用于對該事件或條件進行響應。

結合這幅圖和上面對回調函數的解釋,我們可以發現,要實現回調函數,最關鍵的一點就是要將函數的指針傳遞給一個函數(上圖中是庫函數),然后這個函數就可以通過這個指針來調用回調函數了。注意,回調函數并不是C語言特有的,幾乎任何語言都有回調函數。在C語言中,我們通過使用函數指針來實現回調函數。
回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用于對該事件或條件進行響應。
2. 為什么要用回調函數?
因為可以把調用者與被調用者分開,所以調用者不關心誰是被調用者。它只需知道存在一個具有特定原型和限制條件的被調用函數。

int?Callback()????// /
{
????//?TODO
????return?0;
}
int?main()?????// /?主函數
{
????//?TODO
????Library(Callback);??// /
????//?TODO
????return?0;
}回調似乎只是函數間的調用,和普通函數調用沒啥區別。
3. 怎么使用回調函數?
int?Callback_1(int?a)???// /
{
????printf("Hello,?this?is?Callback_1:?a?=?%d?",?a);
????return?0;
}
int?Callback_2(int?b)??// /
{
????printf("Hello,?this?is?Callback_2:?b?=?%d?",?b);
????return?0;
}
int?Callback_3(int?c)???// /
{
????printf("Hello,?this?is?Callback_3:?c?=?%d?",?c);
????return?0;
}
int?Handle(int?x,?int?(*Callback)(int))??// /
{
????Callback(x);
}
int?main()
{
????Handle(4,?Callback_1);
????Handle(5,?Callback_2);
????Handle(6,?Callback_3);
????return?0;
}#include?
#include?
/****************************************
?*?函數指針結構體
?***************************************/
typedef?struct?_OP?
{????float?(*p_add)(float,?float);?
????float?(*p_sub)(float,?float);?
????float?(*p_mul)(float,?float);?
????float?(*p_div)(float,?float);?
}?OP;?
/****************************************
?*?加減乘除函數
?***************************************/
float?ADD(float?a,?float?b)?
{
????return?a?+?b;
}
float?SUB(float?a,?float?b)?
{
????return?a?-?b;
}
float?MUL(float?a,?float?b)?
{
????return?a?*?b;
}
float?DIV(float?a,?float?b)?
{
????return?a?/?b;
}
/****************************************
?*?初始化函數指針
?***************************************/
void?init_op(OP?*op)
{
????op->p_add?=?ADD;
????op->p_sub?=?SUB;
????op->p_mul?=?&MUL;
????op->p_div?=?÷
}
/****************************************
?*?庫函數
?***************************************/
float?add_sub_mul_div(float?a,?float?b,?float?(*op_func)(float,?float))
{
????return?(*op_func)(a,?b);
}
int?main(int?argc,?char?*argv[])?
{
????OP?*op?=?(OP?*)malloc(sizeof(OP));?
????init_op(op);
????
????/*?直接使用函數指針調用函數?*/?
????printf("ADD?=?%f,?SUB?=?%f,?MUL?=?%f,?DIV?=?%f\n",?(op->p_add)(1.3,?2.2),?(*op->p_sub)(1.3,?2.2),?
????????????(op->p_mul)(1.3,?2.2),?(*op->p_div)(1.3,?2.2));
?????
????/*?調用回調函數?*/?
????printf("ADD?=?%f,?SUB?=?%f,?MUL?=?%f,?DIV?=?%f\n",?
????????????add_sub_mul_div(1.3,?2.2,?ADD),?
????????????add_sub_mul_div(1.3,?2.2,?SUB),?
????????????add_sub_mul_div(1.3,?2.2,?MUL),?
????????????add_sub_mul_div(1.3,?2.2,?DIV));
????return?0;?
}
/*********??工作狀態處理??*********/
typedef?struct
{
????uint8_t?mStatus;
????uint8_t?(*?Funtion)(void);?//函數指針的形式
}?M26_WorkStatus_TypeDef;???//M26的工作狀態集合調用函數
/**********************************************
**?>M26工作狀態集合函數
***********************************************/
M26_WorkStatus_TypeDef?M26_WorkStatus_Tab[]?=
{????
????{GPRS_NETWORK_CLOSE,??M26_PWRKEY_Off??},????//模塊關機
????{GPRS_NETWORK_OPEN,??M26_PWRKEY_On??},??????//模塊開機
????{GPRS_NETWORK_Start,???M26_Work_Init??},????//管腳初始化
????{GPRS_NETWORK_CONF,??M26_NET_Config??},?????//AT指令配置
????{GPRS_NETWORK_LINK_CTC,??M26_LINK_CTC??},???//連接調度中心??
????{GPRS_NETWORK_WAIT_CTC,?M26_WAIT_CTC??},????//等待調度中心回復?
????{GPRS_NETWORK_LINK_FEM,?M26_LINK_FEM??},????//連接前置機
????{GPRS_NETWORK_WAIT_FEM,?M26_WAIT_FEM??},????//等待前置機回復
????{GPRS_NETWORK_COMM,??M26_COMM???},??????????//正常工作????
????{GPRS_NETWORK_WAIT_Sig,??M26_WAIT_Sig??},???//等待信號回復
????{GPRS_NETWORK_GetSignal,??M26_GetSignal??},?//獲取信號值
????{GPRS_NETWORK_RESTART,??M26_RESET???},??????//模塊重啟
}
/**********************************************
**?>M26模塊工作狀態機,依次調用里面的12個函數???
***********************************************/
uint8_t?M26_WorkStatus_Call(uint8_t?Start)
{
????uint8_t?i?=?0;
????for(i?=?0;?i?12;?i++)
????{
????????if(Start?==?M26_WorkStatus_Tab[i].mStatus)
????????{??????????
??????return?M26_WorkStatus_Tab[i].Funtion();
????????}
????}
????return?0;
}最近極客時間出了一個《MySQL底層原理精講》的專欄,目前還在內測階段,主要是看市場反饋來定價,所以現在還是免費階段,等上線了估計就可能收費了。
一次吃透 MySQL 底層原理?? 架構篇、事務篇、索引與鎖篇全覆蓋
這里分享一下極客時間的這份內測專欄,只申請到30個內測名額,掃描下方二維碼自取。。。

掃描上方二維碼自領
—— EOF —— 你好,我是飛宇。日常分享C/C++、計算機學習經驗、工作體會,歡迎點擊此處查看我以前的學習筆記&經驗&分享的資源。
我組建了一些社群一起交流,群里有大牛也有小白,如果你有意可以一起進群交流。
歡迎你添加我的微信,我拉你進技術交流群。此外,我也會經常在微信上分享一些計算機學習經驗以及工作體驗,還有一些內推機會。
加個微信,打開另一扇窗
經常遇到有讀者后臺私信想要一些編程學習資源,這里分享?1T 的編程電子書、C/C++開發手冊、Github上182K+的架構路線圖、LeetCode算法刷題筆記等精品學習資料,點擊下方公眾號會回復"編程"即可免費領取~
感謝你的分享,點贊,在看三連??