新四季網

學習c語言指針函數後的感悟(指針進階第五站)

2023-10-11 10:14:05

函數指針

函數也有自己的地址,函數名/&函數名 就是函數的地址

1.1基本形式

在 數組指針的學習中我們了解到

int arr[5];int (*pa)[5] = //pa是數組指針

指針變量pa的類型是int(*)[5]

那麼函數指針的形式是怎樣的呢?

void test(char* str){}int main{ void (*pt)(char*) = test; //pt是一個函數指針 return 0;}

pt的類型是void (*)(char*)

下面哪個代碼有能力存放函數的地址呢?

void (*pfun1);void *pfun2;

答:pfun1可以存放

pfun1先和*結合,說明pfun1是指針,指針指向的是一個函數,指向的函數無 參數,返回值類型為void

pfun2先和結合,判斷為一個返回值為int*類型的函數

那麼,如何書寫一個函數指針呢?

int Add(int x, int y){ return x y;}

以Add函數為例,它有兩個int類型的形參,返回類型是int

所對應的函數指針就是int(*)(int,int)類型

int (*pf)(int, int) = Add;

依據以下幾步就能正確寫出函數指針

(1)確定函數的返回類型

(2)確定函數的參數類型和個數

(3)把函數參數類型裡的變量名去掉,放入括號裡

(int x,int y)去掉x、y,即(int,int)

(4)在前面加上函數的返回類型

(5)最後加上(*),以及函數指針變量名

需要注意的是,(*pf)的括號不能省略,否則編譯器會報錯

去掉括號之後就相當於函數聲明,無法賦值

1.2用函數指針來調用函數

如下圖所示,當我們定義了一個函數指針後

就可以通過指針來訪問原函數

這時候(*pf)其實就相當於my_test

我們可以通過函數指針來調用上面提到過的Add函數

​int Add(int x, int y){ return x y;}​int main{ int (* pf)(int, int) = Add; int sum = (*pf)(2,3); int sum1 = pf(2, 3);​ int sum2 = Add(2, 3); printf("%d\n", sum); printf("%d\n", sum1); printf("%d\n", sum2);​ return 0;}​

可以看到,sum和sum1兩種形式都正確調用了該函數

因為我們已經把Add的地址轉給了pf指針,函數名Add和指針pf實際上是等價的

所以在使用函數指針的時候,可以不帶*使用。但是帶*的時候一定要加括號!

1.3兩個奇葩的代碼

奇葩代碼1

(*(void (*))0);

這裡的0僅為示例,我們在正常使用的時候並不能訪問0的地址

看到這個代碼的時候,是不是有點懵?

別急,讓我們來慢慢分析一波!

奇葩代碼2

void (*signal(int , void(*)(int)))(int);

說人話就是,signal函數內傳入了一個void(*)(int)的函數指針,返回值也是一個void(*)(int)的函數指針!

void fun(int num){ printf("fun-->%d\n", num);}void ( *signal( int, void(*)(int) ) )(int);​int main{ void(*pf)(int);//定義一個函數指針 pf = signal(100, fun); //為signal函數傳參,並用pf指針接收 return 0;}

「這個代碼2是真的奇葩,就沒有什麼辦法把他變成人話嗎?(簡化一下)」

當然有!那就是用typedef函數來給void(*)(int)指針起一個新名字!

typedef void(*pf_t)(int);//把void(*)(int)命名為pf_t

void(*p)(int);//p是函數指針變量的名字typedef void(*pf_t)(int);//pf_t是一個新的類型名

這樣我們的代碼就能得到簡化

void ( *signal( int, void(*)(int) ) )(int);//原始碼//簡化後pf_t siganal(int,pf_t);

這樣是不是就更容易分辨了?

2函數指針數組2.1基本形式

既然函數指針也是一個指針類型,那我們就可以用指針數組來存放它

前提:這些函數的參數類型、返回類型一致

int Add(int x, int y){ return x y;}int Sub(int x, int y){ return x - y;}int Mul(int x, int y){ return x * y;}int Div(int x, int y){ return x / y;}//函數指針數組int (*pfArr[4])(int, int) = {Add, Sub, Mul, Div};​

相比於分開寫多次函數調用

//多次函數調用int (*pf1)(int,int) = Add;int (*pf2)(int, int) = Sub;int (*pf3)(int, int) = Mul;int (*pf4)(int, int) = Div;

函數指針數組可以讓我們以使用數組的形式來訪問每個函數

int Add(int x, int y){ return x y;}int Sub(int x, int y){ return x - y;}int Mul(int x, int y){ return x * y;}int Div(int x, int y){ return x / y;}​int main{ int (*pfArr[4])(int, int) = {Add, Sub, Mul, Div};//函數指針數組 int i = 0; for (i = 0; i "); scanf("%d", &input); switch (input) { case 1: printf("輸入2個操作數:>"); scanf("%d %d", &x, &y); ret = Add(x, y); printf("ret = %d\n", ret); break; case 2: printf("輸入2個操作數:>"); scanf("%d %d", &x, &y); ret = Sub(x, y); printf("ret = %d\n", ret); break; case 3: printf("輸入2個操作數:>"); scanf("%d %d", &x, &y); ret = Mul(x, y); printf("ret = %d\n", ret); break; case 4: printf("輸入2個操作數:>"); scanf("%d %d", &x, &y); ret = Div(x, y); printf("ret = %d\n", ret); break; case 0: printf("退出計算器\n"); break; default: printf("選擇錯誤\n"); break; } } while (input);​ return 0;}​

這種方式需要寫非常多的重複代碼,而且代碼長度很長????

我們可以使用函數指針對它進行優化

2.2.2函數指針形式

int main{ int input = 0; int x = 0; int y = 0; int ret = 0;​ int (*pfArr[5])(int, int) = {0, Add, Sub, Mul, Div}; //pfArr是一個函數指針的數組,也叫轉移表 do { menu; printf("請選擇:>"); scanf("%d", &input); if (input == 0) { printf("退出計算器\n"); break; } else if (input >= 1 && input "); scanf("%d %d", &x, &y); ret = pfArr[input](x, y); printf("ret = %d\n", ret); } else { printf("選擇錯誤\n"); } } while (input);​ return 0;}​

這樣就避免了我們在每個case語句裡都寫上輸入提示、scanf和不同的函數調用所導致的代碼冗餘了

運行試試吧!

2.3指向函數指針數組的指針

函數指針數組是一個數組,數組可以用數組指針來存放地址

指向函數指針數組的指針:是一個指針

該指針指向一個數組,數組的每個元素都是一個函數指針

int Add(int x, int y){ return x y;}int main{ int (*pa)(int, int) = Add;//函數指針 int (* pfA[4])(int, int);//函數指針的數組 int (* (*ppfA)[4])(int, int) = //ppfA 是一個指針,該指針指向了一個存放函數指針的數組 return 0;}

3.相關練習題

定義一個函數指針,指向的函數有兩個int形參並且返回一個函數指針,返回的指針指向一個有一個int形參且返回int的函數?下面哪個是正確的?

A. int (*(*F)(int, int))(int)B. int (*F)(int, int)C. int (*(*F)(int, int))D. *(*F)(int, int)(int)

一步步分析題目的要求

該函數指針指向的函數有兩個int類型,即(int,int),ABCD都有,無法排除

仔細看看,D的類型沒有寫全,直接排除

返回一個函數指針,該指針指向一個有一個int形參且返回int的函數

B是一個函數指針,返回類型是int,錯誤

C的返回值是int*類型,錯誤

A選項去掉函數指針F後,剩下int (*)(int),符合題意

結語

你學廢了嗎?

-----------------------------------

為了幫助大家,輕鬆,高效學習C語言/C ,給大家分享我收集的資源,從最零基礎開始的,幫助大家在學習C語言的道路上披荊斬棘!

編程學習書籍分享:

編程學習視頻分享:

整理分享(多年學習的源碼、項目實戰視頻、項目筆記,基礎入門教程)

對於C/C 感興趣可以關注小編在後臺私信我:【編程交流】一起來學習哦!可以領取一些C/C 的項目學習視頻資料哦!已經設置好了關鍵詞自動回復,自動領取就好了!

,
同类文章
葬禮的夢想

葬禮的夢想

夢見葬禮,我得到了這個夢想,五個要素的五個要素,水火只好,主要名字在外面,職業生涯良好,一切都應該對待他人治療誠意,由於小,吉利的冬天夢想,秋天的夢是不吉利的
找到手機是什麼意思?

找到手機是什麼意思?

找到手機是什麼意思?五次選舉的五個要素是兩名士兵的跡象。與他溝通很好。這是非常財富,它擅長運作,職業是仙人的標誌。單身男人有這個夢想,主要生活可以有人幫忙
我不怎麼想?

我不怎麼想?

我做了什麼意味著看到米飯烹飪?我得到了這個夢想,五線的主要土壤,但是Tu Ke水是錢的跡象,職業生涯更加真誠。他真誠地誠實。這是豐富的,這是夏瑞的巨星
夢想你的意思是什麼?

夢想你的意思是什麼?

你是什​​麼意思夢想的夢想?夢想,主要木材的五個要素,水的跡象,主營業務,主營業務,案子應該抓住魅力,不能疏忽,春天夢想的吉利夢想夏天的夢想不幸。詢問學者夢想
拯救夢想

拯救夢想

拯救夢想什麼意思?你夢想著拯救人嗎?拯救人們的夢想有一個現實,也有夢想的主觀想像力,請參閱週宮官方網站拯救人民夢想的詳細解釋。夢想著敵人被拯救出來
2022愛方向和生日是在[質量個性]中

2022愛方向和生日是在[質量個性]中

[救生員]有人說,在出生88天之前,胎兒已經知道哪天的出生,如何有優質的個性,將走在什麼樣的愛情之旅,將與生活生活有什么生活。今天
夢想切割剪裁

夢想切割剪裁

夢想切割剪裁什麼意思?你夢想切你的手是好的嗎?夢想切割手工切割手有一個真正的影響和反應,也有夢想的主觀想像力。請參閱官方網站夢想的細節,以削減手
夢想著親人死了

夢想著親人死了

夢想著親人死了什麼意思?你夢想夢想你的親人死嗎?夢想有一個現實的影響和反應,還有夢想的主觀想像力,請參閱夢想世界夢想死亡的親屬的詳細解釋
夢想搶劫

夢想搶劫

夢想搶劫什麼意思?你夢想搶劫嗎?夢想著搶劫有一個現實的影響和反應,也有夢想的主觀想像力,請參閱週恭吉夢官方網站的詳細解釋。夢想搶劫
夢想缺乏缺乏紊亂

夢想缺乏缺乏紊亂

夢想缺乏缺乏紊亂什麼意思?你夢想缺乏異常藥物嗎?夢想缺乏現實世界的影響和現實,還有夢想的主觀想像,請看官方網站的夢想組織缺乏異常藥物。我覺得有些東西缺失了