?
函數(shù)指針
函數(shù)也有自己的地址,函數(shù)名/&函數(shù)名 就是函數(shù)的地址

1.1基本形式
在?數(shù)組指針的學(xué)習(xí)中我們了解到
int arr[5];int (*pa)[5] = &arr;//pa是數(shù)組指針
?指針變量pa的類(lèi)型是int(*)[5]
那么函數(shù)指針的形式是怎樣的呢?
void test(char* str){}int main(){void (*pt)(char*) = test;//pt是一個(gè)函數(shù)指針return 0;}
pt的類(lèi)型是void (*)(char*)
下面哪個(gè)代碼有能力存放函數(shù)的地址呢?
void (*pfun1)();void *pfun2();
答:pfun1可以存放
pfun1先和*結(jié)合,說(shuō)明pfun1是指針,指針指向的是一個(gè)函數(shù),指向的函數(shù)無(wú) 參數(shù),返回值類(lèi)型為void
pfun2先和()結(jié)合,判斷為一個(gè)返回值為int*類(lèi)型的函數(shù)
那么,如何書(shū)寫(xiě)一個(gè)函數(shù)指針呢?
int Add(int x, int y){return x + y;}
以Add函數(shù)為例,它有兩個(gè)int類(lèi)型的形參,返回類(lèi)型是int
所對(duì)應(yīng)的函數(shù)指針就是int(*)(int,int)類(lèi)型
int (*pf)(int, int) = Add;
依據(jù)以下幾步就能正確寫(xiě)出函數(shù)指針
(1)確定函數(shù)的返回類(lèi)型
(2)確定函數(shù)的參數(shù)類(lèi)型和個(gè)數(shù)
(3)把函數(shù)參數(shù)類(lèi)型里的變量名去掉,放入括號(hào)里
????????(int x,int y)去掉x、y,即(int,int)
(4)在前面加上函數(shù)的返回類(lèi)型
(5)最后加上(*),以及函數(shù)指針變量名
需要注意的是,(*pf)的括號(hào)不能省略,否則編譯器會(huì)報(bào)錯(cuò)
去掉括號(hào)之后就相當(dāng)于函數(shù)聲明,無(wú)法賦值

?
1.2用函數(shù)指針來(lái)調(diào)用函數(shù)
如下圖所示,當(dāng)我們定義了一個(gè)函數(shù)指針后
就可以通過(guò)指針來(lái)訪問(wèn)原函數(shù)
這時(shí)候(*pf)其實(shí)就相當(dāng)于my_test

我們可以通過(guò)函數(shù)指針來(lái)調(diào)用上面提到過(guò)的Add函數(shù)
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 ", sum);printf("%d ", sum1);printf("%d ", sum2);return 0;}
可以看到,sum和sum1兩種形式都正確調(diào)用了該函數(shù)

因?yàn)槲覀円呀?jīng)把Add的地址轉(zhuǎn)給了pf指針,函數(shù)名Add和指針pf實(shí)際上是等價(jià)的
所以在使用函數(shù)指針的時(shí)候,可以不帶*使用。但是帶*的時(shí)候一定要加括號(hào)!
?
1.3兩個(gè)奇葩的代碼
奇葩代碼1
(*(void (*)())0)();
這里的0僅為示例,我們?cè)谡J褂玫臅r(shí)候并不能訪問(wèn)0的地址
看到這個(gè)代碼的時(shí)候,是不是有點(diǎn)懵?
別急,讓我們來(lái)慢慢分析一波!

奇葩代碼2
void (*signal(int , void(*)(int)))(int);

說(shuō)人話(huà)就是,signal函數(shù)內(nèi)傳入了一個(gè)void(*)(int)的函數(shù)指針,返回值也是一個(gè)void(*)(int)的函數(shù)指針!
void?fun(int?num){printf("fun-->%d ", num);}void ( *signal( int, void(*)(int) ) )(int);int main(){void(*pf)(int);//定義一個(gè)函數(shù)指針pf = signal(100, fun);//為signal函數(shù)傳參,并用pf指針接收return 0;}
“這個(gè)代碼2是真的奇葩,就沒(méi)有什么辦法把他變成人話(huà)嗎?(簡(jiǎn)化一下)”
當(dāng)然有!那就是用typedef函數(shù)來(lái)給void(*)(int)指針起一個(gè)新名字!
typedef void(*pf_t)(int);//把void(*)(int)命名為pf_t
void(*p)(int);//p是函數(shù)指針變量的名字typedef void(*pf_t)(int);//pf_t是一個(gè)新的類(lèi)型名
這樣我們的代碼就能得到簡(jiǎn)化
void ( *signal( int, void(*)(int) ) )(int);//源代碼//簡(jiǎn)化后pf_t siganal(int,pf_t);
這樣是不是就更容易分辨了?
?
2函數(shù)指針數(shù)組
2.1基本形式
既然函數(shù)指針也是一個(gè)指針類(lèi)型,那我們就可以用指針數(shù)組來(lái)存放它
前提:這些函數(shù)的參數(shù)類(lèi)型、返回類(lèi)型一致
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;}//函數(shù)指針數(shù)組int?(*pfArr[4])(int,?int)?=?{Add,?Sub,?Mul,?Div};
相比于分開(kāi)寫(xiě)多次函數(shù)調(diào)用
//多次函數(shù)調(diào)用int (*pf1)(int,int) = Add;int (*pf2)(int, int) = Sub;int (*pf3)(int, int) = Mul;int (*pf4)(int, int) = Div;
函數(shù)指針數(shù)組可以讓我們以使用數(shù)組的形式來(lái)訪問(wèn)每個(gè)函數(shù)
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};//函數(shù)指針數(shù)組int i = 0;for (i = 0; i < 4; i++){//int ret = (*pfArr[i])(8, 4);int ret = pfArr[i](8, 4);printf("%d ", ret);}return 0;}
這樣也簡(jiǎn)化了我們的代碼
?
2.2、計(jì)算器實(shí)現(xiàn)
2.2.1switch語(yǔ)句
目的:實(shí)現(xiàn)一個(gè)計(jì)算器
菜單:用數(shù)字來(lái)選擇運(yùn)算類(lèi)型
方法:以switch/case語(yǔ)句來(lái)實(shí)現(xiàn)函數(shù)調(diào)用
結(jié)束:用do/while實(shí)現(xiàn)多組輸入,以及結(jié)束程序
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;}void menu(){printf("********************************** ");printf("***** 1. add 2. sub ***** ");printf("***** 3. mul 4. div ***** ");printf("***** 0. exit ***** ");printf("********************************** ");}int main(){int input = 0;int x = 0;int y = 0;int ret = 0;do{menu();printf("請(qǐng)選擇:>");scanf("%d", &input);switch (input){case 1:printf("輸入2個(gè)操作數(shù):>");scanf("%d %d", &x, &y);ret = Add(x, y);printf("ret = %d ", ret);break;case 2:printf("輸入2個(gè)操作數(shù):>");scanf("%d %d", &x, &y);ret = Sub(x, y);printf("ret = %d ", ret);break;case 3:printf("輸入2個(gè)操作數(shù):>");scanf("%d %d", &x, &y);ret = Mul(x, y);printf("ret = %d ", ret);break;case 4:printf("輸入2個(gè)操作數(shù):>");scanf("%d %d", &x, &y);ret = Div(x, y);printf("ret = %d ", ret);break;case 0:printf("退出計(jì)算器 ");break;default:printf("選擇錯(cuò)誤 ");break;}} while (input);return 0;}
這種方式需要寫(xiě)非常多的重復(fù)代碼,而且代碼長(zhǎng)度很長(zhǎng)????
我們可以使用函數(shù)指針對(duì)它進(jìn)行優(yōu)化
2.2.2函數(shù)指針形式
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是一個(gè)函數(shù)指針的數(shù)組,也叫轉(zhuǎn)移表do{menu();printf("請(qǐng)選擇:>");scanf("%d", &input);if (input == 0){printf("退出計(jì)算器 ");break;}else if (input >= 1 && input <= 4){printf("輸入2個(gè)操作數(shù):>");scanf("%d %d", &x, &y);ret = pfArr[input](x, y);printf("ret = %d ", ret);}else{printf("選擇錯(cuò)誤 ");}} while (input);return 0;}
這樣就避免了我們?cè)诿總€(gè)case語(yǔ)句里都寫(xiě)上輸入提示、scanf和不同的函數(shù)調(diào)用所導(dǎo)致的代碼冗余了
運(yùn)行試試吧!

2.3指向函數(shù)指針數(shù)組的指針
函數(shù)指針數(shù)組是一個(gè)數(shù)組,數(shù)組可以用數(shù)組指針來(lái)存放地址
指向函數(shù)指針數(shù)組的指針:是一個(gè)指針
該指針指向一個(gè)數(shù)組,數(shù)組的每個(gè)元素都是一個(gè)函數(shù)指針
int?Add(int?x,?int?y){return x + y;}int main(){int (*pa)(int, int) = Add;//函數(shù)指針int (* pfA[4])(int, int);//函數(shù)指針的數(shù)組int (* (*ppfA)[4])(int, int) = &pfA;//ppfA 是一個(gè)指針,該指針指向了一個(gè)存放函數(shù)指針的數(shù)組return 0;}
3.相關(guān)練習(xí)題
定義一個(gè)函數(shù)指針,指向的函數(shù)有兩個(gè)int形參并且返回一個(gè)函數(shù)指針,返回的指針指向一個(gè)有一個(gè)int形參且返回int的函數(shù)?下面哪個(gè)是正確的?
A. int (*(*F)(int, int))(int)B. int (*F)(int, int)C. int (*(*F)(int, int))D. *(*F)(int, int)(int)
一步步分析題目的要求
該函數(shù)指針指向的函數(shù)有兩個(gè)int類(lèi)型,即(int,int),ABCD都有,無(wú)法排除
仔細(xì)看看,D的類(lèi)型沒(méi)有寫(xiě)全,直接排除
返回一個(gè)函數(shù)指針,該指針指向一個(gè)有一個(gè)int形參且返回int的函數(shù)
B是一個(gè)函數(shù)指針,返回類(lèi)型是int,錯(cuò)誤
C的返回值是int*類(lèi)型,錯(cuò)誤
A選項(xiàng)去掉函數(shù)指針F后,剩下int (*)(int),符合題意
結(jié)語(yǔ)
你學(xué)廢了嗎?
審核編輯 :李倩
電子發(fā)燒友App



























評(píng)論