C語(yǔ)言中的函數(shù)指針學(xué)習(xí)筆記
一、定義函數(shù)指針
return_type (*func_pointer)(parameter_list)
普通指針變量的定義
int * p; char * pointer;
類型的限定都在變量前面;
函數(shù)指針類型的限定是前后都有,前面是返回類型,后面是輸入?yún)?shù)。

利用typedef 可以簡(jiǎn)化上面的表達(dá)方式。
typedef return_type (*FunctionPointer) (parameter_list); FunctionPointer func_pointer;
這樣是不是容易讀了,和上面的功能一樣,定義了一個(gè)返回類型為return_type ,輸入?yún)?shù)為parameter_list的函數(shù)指針。
二、定義返回函數(shù)指針的函數(shù)
return_type(*function(func_parameter_list))(parameter_list)

方框圈出來(lái)的表示返回類型為函數(shù)指針,剩下的部分就表示一個(gè)function函數(shù),輸入?yún)?shù)為func_parameter_list。
它就等價(jià)于 FunctionPointer function(func_parameter_list); 。
再看看:
void ( *signal( int sig, void (* handler)( int )))( int );

signal是一個(gè)返回函數(shù)指針的函數(shù),signal的輸入為int 變量和一個(gè)函數(shù)指針。
三、函數(shù)指針的使用
#include <stdio.h>
int add(int a, int b);
void main()
{
int(*fun1)(int a, int b) = add;
int(*fun2)(int a, int b) = &add;
int(*fun3)(int a, int b) = *add;
printf("%d\n", fun1(1, 2));
printf("%d\n", fun2(1, 2));
printf("%d\n", fun3(1, 2));
char input[10];
gets(input);
}
int add(int a, int b)
{
return a + b;
}
函數(shù)名會(huì)被隱式的轉(zhuǎn)變?yōu)橹羔?,前面?和&操作符都不起作用,printf的結(jié)果都是3。
四、神奇的代碼
int (*(*pf())())()
{ return nullptr; }
哇哦,這是個(gè)什么函數(shù)!畫(huà)個(gè)框框分解它

小框表示返回的是一個(gè)函數(shù)指針,在圈個(gè)大框,又是一個(gè)函數(shù)指針。
它就表示,pf() 返回的是一個(gè)函數(shù)指針,這個(gè)函數(shù)指針對(duì)應(yīng)一個(gè)無(wú)輸入?yún)?shù)的函數(shù):返回值也是函數(shù)指針(對(duì)應(yīng)無(wú)輸入?yún)?shù)的函數(shù),返回值為int類型)。好復(fù)雜啊,有點(diǎn)暈!
利用typedef 簡(jiǎn)化一下。
typedef int(*Fun1) ();
typedef Fun1(*Fun2) ();
Fun2 pf()
{
return nullptr;
}
這樣看就舒服多了。
五、這又是什么鬼!
(*(void(*) ())0)();
畫(huà)個(gè)框看看:

小框里代表一個(gè)函數(shù)指針,常數(shù)前面加括號(hào)代表類型的強(qiáng)制轉(zhuǎn)換。咦,它把0強(qiáng)制轉(zhuǎn)換成了一個(gè)函數(shù)指針,并執(zhí)行!這是什么操作??!
六、一段驗(yàn)證代碼
#include <stdio.h>
typedef int Function(int, int);
typedef int(*FunctionPointer1) (int, int);
typedef FunctionPointer1(*FunctionPointer2) ();
int fun1(int a, int b)
{
return a + b;
}
FunctionPointer1 fun2()
{
return fun1;
}
FunctionPointer2 fun3()
{
return fun2;
}
int(*(*fun4())())(int, int)
{
return fun2;
}
void main()
{
Function* fuction = fun1;
FunctionPointer1 fun = fun1;
int a = fun3()()(3, 4);
int b = fun4()()(5, 6);
printf("%d\n%d\n", a, b);
printf("fun1:%d\n*fun1:%d\n&fun1:%d", fun1, *fun1, &fun1);
printf("fun:%d\n*fun:%d\n&fun:%d", fun, *fun, &fun);
char chars[10];
gets(chars);
}
函數(shù)名前面加不加*,&操作符,都是一個(gè)效果;函數(shù)指針前面加不加*操作符是一個(gè)效果,但是加上&操作符就代表著取指針的地址了。
可以通過(guò)typedef int Function(int, int); 為一種類型的函數(shù)定義別名,但是使用的時(shí)候只能定義指針形式的變量:
Function* fuction = fun1;
七、一個(gè)問(wèn)題
在stackoverflow上偶爾看到如下的問(wèn)題,代碼如下
#include
void hello() { printf("hello"); }
int hello_1()
{
printf("hello 1");
return 0;
}
int main(void) {
(*****hello)();
(****hello_1)();
}
執(zhí)行結(jié)果是無(wú)論hello前面有多少個(gè)指針?lè)?hào),都是執(zhí)行hello()函數(shù),打印“hello”。
為什么出現(xiàn)這樣的結(jié)果呢:
用指針指向一個(gè)函數(shù)是OK的,但是仍然還要被轉(zhuǎn)化為一個(gè)function pointer。其實(shí)使用*來(lái)指向一個(gè)函數(shù) == CALL這個(gè)函數(shù)。因此無(wú)論指向多少次,仍然也是調(diào)用這個(gè)函數(shù)。
為什么一個(gè)函數(shù)會(huì)被轉(zhuǎn)化成一個(gè)指針呢?答案就是將函數(shù)默認(rèn)的轉(zhuǎn)換成函數(shù)指針,可以減少&的使用,編譯器默認(rèn)的將函數(shù)轉(zhuǎn)化為函數(shù)指針,也省得你每次調(diào)用函數(shù)時(shí)加*調(diào)用函數(shù)。
哈哈,也就是我們之前說(shuō)的,函數(shù)即指針。似乎有點(diǎn)不是很清晰,再看下面的例子
void foo() {
printf("Foo to you too!...\n");
};
int a = 2;
int* test()
{
return &a;
}
int main()
{
int i;
void (*p1_foo)() = foo;
void (*p2_foo)() = *foo;
void (*p3_foo)() = &foo;
void (*p4_foo)() = *&foo;
void (*p5_foo)() = &*foo;
void (*p6_foo)() = **foo;
void (*p7_foo)() = **********************foo;
(*p1_foo)();
(*p2_foo)();
(*p3_foo)();
(*p4_foo)();
(*p5_foo)();
(*p6_foo)();
(*p7_foo)();
i = *(***test)();
printf("i=%d\n",i);
}
上面的列子不出例外,都能正常打印我們想要的數(shù)據(jù)。
但是對(duì)于&,則要進(jìn)行仔細(xì)的分析一下:
&對(duì)于一個(gè)函數(shù)的操作,是返回一個(gè)指針,指向函數(shù)的指針,如果在對(duì)此指針執(zhí)行&也就是&&foo,則會(huì)返回error,因?yàn)?amp;foo是一個(gè)指針數(shù)值,也就是一個(gè)rvalue類型,再對(duì)他進(jìn)行&操作,顯然是返回error的。
&&foo //EROOR &*&*&*&*&*&*foo //OK &******&foo //OK
相關(guān)文章
深入const int *p與int * const p的區(qū)別詳解(常量指針與指向常量的指針)
本篇文章是對(duì)const int *p與int * const p的區(qū)別進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06
C++實(shí)現(xiàn)LeetCode(198.打家劫舍)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(198.打家劫舍),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
QT中進(jìn)程的創(chuàng)建實(shí)現(xiàn)
本文主要介紹了QT中進(jìn)程的創(chuàng)建實(shí)現(xiàn),詳細(xì)介紹了創(chuàng)建進(jìn)程的整個(gè)過(guò)程,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2023-08-08
C++超詳細(xì)講解隱藏私有屬性和方法的兩種實(shí)現(xiàn)方式
為了避免因?yàn)閷㈩悗?kù)中的私有成員開(kāi)放給類的使用方而導(dǎo)致的軟件邏輯外泄,因此需要將對(duì)外代碼中的私有成員隱藏起來(lái),下面我們來(lái)了解一下隱藏私有屬性和方法的兩種實(shí)現(xiàn)方式2022-05-05
讓?xiě)?yīng)用程序只運(yùn)行一個(gè)實(shí)例的實(shí)現(xiàn)方法
我們?cè)谑褂谩?60軟件管家》時(shí)發(fā)現(xiàn),在《360軟件管家》已經(jīng)運(yùn)行了的情況下,再次點(diǎn)擊《360軟件管家》的圖標(biāo),那么它不會(huì)再運(yùn)行另外一個(gè)《360軟件管家》,而是將已有的《360軟件管家》給激活,始終只能運(yùn)行一個(gè)《360軟件管家》的實(shí)例2013-05-05
詳解C語(yǔ)言動(dòng)態(tài)內(nèi)存的分配
這篇文章主要為大家介紹了C語(yǔ)言動(dòng)態(tài)內(nèi)存的分配,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2021-12-12
C/C++ 中g(shù)cc和g++的對(duì)比與區(qū)別
這篇文章主要介紹了C/C++ 中g(shù)cc和g++的對(duì)比與區(qū)別的相關(guān)資料,需要的朋友可以參考下2017-07-07

