文章目錄
- 1函數指針
- 1.1基本形式
- 1.2用函數指針來調用函數
- 1.3兩個奇葩的代碼
- 奇葩代碼1
- 奇葩代碼2
- 2函數指針數組
- 2.1基本形式
- 2.2電腦實作
- 2.2.1switch語句
- 2.2.2函數指針形式
- 2.3指向函數指針數組的指針
- 3.相關練習題
- 結語
指針進階第五站????:函數指針
點進我的首頁,可以回顧前四站的内容哦~
1函數指針
函數也有自己的位址,函數名/&函數名 就是函數的位址
1.1基本形式
在數組指針的學習中我們了解到
int arr[5];
int (*pa)[5] = &arr;//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;
依據以下幾步就能正确寫出函數指針
- 确定函數的傳回類型
- 确定函數的參數類型和個數
-
把函數參數類型裡的變量名去掉,放入括号裡
去掉x、y,即(int x,int y)
(int,int)
- 在前面加上函數的傳回類型
- 最後加上
,以及函數指針變量名(*)
需要注意的是,
(*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 < 4; i++)
{
//int ret = (*pfArr[i])(8, 4);
int ret = pfArr[i](8, 4);
printf("%d\n", ret);
}
return 0;
}
這樣也簡化了我們的代碼
2.2電腦實作
2.2.1switch語句
- 目的:實作一個電腦
- 菜單:用數字來選擇運算類型
- 方法:以switch/case語句來實作函數調用
- 結束:用do/while實作多組輸入,以及結束程式
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("**********************************\n");
printf("***** 1. add 2. sub *****\n");
printf("***** 3. mul 4. div *****\n");
printf("***** 0. exit *****\n");
printf("**********************************\n");
}
int main()
{
int input = 0;
int x = 0;
int y = 0;
int ret = 0;
do
{
menu();
printf("請選擇:>");
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 <= 4)
{
printf("輸入2個操作數:>");
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) = &pfA;
//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類型,即
,ABCD都有,無法排除(int,int)
- 仔細看看,D的類型沒有寫全,直接排除
- 傳回一個函數指針,該指針指向一個
有一個int形參且傳回int的函數
B是一個函數指針,傳回類型是int,錯誤
C的傳回值是int*類型,錯誤
- A選項去掉函數指針F後,剩下
,符合題意int (*)(int)
結語
函數指針的知識點是第一次接觸到????
你學廢了嗎?????
下一站????:回調函數