深入解析C語(yǔ)言中函數(shù)指針的定義與使用
1.函數(shù)指針的定義
函數(shù)是由執(zhí)行語(yǔ)句組成的指令序列或者代碼,這些代碼的有序集合根據(jù)其大小被分配到一定的內(nèi)存空間中,這一片內(nèi)存空間的起始地址就成為函數(shù)的地址,不同的函數(shù)有不同的函數(shù)地址,編譯器通過(guò)函數(shù)名來(lái)索引函數(shù)的入口地址,為了方便操作類(lèi)型屬性相同的函數(shù),c/c++引入了函數(shù)指針,函數(shù)指針就是指向代碼入口地址的指針,是指向函數(shù)的指針變量。 因而“函數(shù)指針”本身首先應(yīng)該是指針變量,只不過(guò)該指針變量指向函數(shù)。這正如用指針變量可指向整形變量、字符型、數(shù)組一樣,這里是指向函數(shù)。C在編譯時(shí),每一個(gè)函數(shù)都有一個(gè)入口地址,該入口地址就是函數(shù)指針?biāo)赶虻牡刂?。有了指向函?shù)的指針變量后,可用該指針變量調(diào)用函數(shù),就如同用指針變量可引用其他類(lèi)型變量一樣,在這些概念上是一致的。函數(shù)指針有兩個(gè)用途:調(diào)用函數(shù)和做函數(shù)的參數(shù)。
函數(shù)指針的聲明方法為:
數(shù)據(jù)類(lèi)型標(biāo)志符 (指針變量名) (形參列表);
“函數(shù)類(lèi)型”說(shuō)明函數(shù)的返回類(lèi)型,由于“()”的優(yōu)先級(jí)高于“*”,所以指針變量名外的括號(hào)必不可少,后面的“形參列表”表示指針變量指向的函數(shù)所帶的參數(shù)列表。例如:
int function(int x,int y); /* 聲明一個(gè)函數(shù) */ int (*f) (int x,int y); /* 聲明一個(gè)函數(shù)指針 */ f=function; /* 將function函數(shù)的首地址賦給指針f */
賦值時(shí)函數(shù)function不帶括號(hào),也不帶參數(shù),由于function代表函數(shù)的首地址,因此經(jīng)過(guò)賦值以后,指針f就指向函數(shù)function(int x,int y);的代碼的首地址。
2.函數(shù)指針使用的例子
知道了如何定義一個(gè)函數(shù)指針,但如何來(lái)使用它呢?先看如下例子:
#include <stdio.h> #include <string.h> char * fun(char * p1,char * p2) { int i = 0; i = strcmp(p1,p2); if (0 == i) { return p1; } else { return p2; } } int main() { char * (*pf)(char * p1,char * p2); pf = &fun; (*pf) ("aa","bb"); return 0; }
我們使用指針的時(shí)候,需要通過(guò)鑰匙(“*”)來(lái)取其指向的內(nèi)存里面的值,函數(shù)指針使用也如此。通過(guò)用(*pf)取出存在這個(gè)地址上的函數(shù),然后調(diào)用它。
這里需要注意到是,在Visual C++6.0里,給函數(shù)指針賦值時(shí),可以用&fun或直接用函數(shù)名fun。這是因?yàn)楹瘮?shù)名被編譯之后其實(shí)就是一個(gè)地址,所以這里兩種用法沒(méi)有本質(zhì)的差別。這個(gè)例子很簡(jiǎn)單,就不再詳細(xì)討論了。
3.*(int*)&p ----這是什么?
也許上面的例子過(guò)于簡(jiǎn)單,我們看看下面的例子:
void Function() { printf("Call Function!\n"); }<br> int main() { void (*p)(); *(int*)&p=(int)Function; (*p)(); return 0; }
這是在干什么?*(int*)&p=(int)Function;表示什么意思?
別急,先看這行代碼:
void (*p)();
這行代碼定義了一個(gè)指針變量p,p指向一個(gè)函數(shù),這個(gè)函數(shù)的參數(shù)和返回值都是void。
&p是求指針變量p本身的地址,這是一個(gè)32位的二進(jìn)制常數(shù)(32位系統(tǒng))。
(int*)&p表示將地址強(qiáng)制轉(zhuǎn)換成指向int類(lèi)型數(shù)據(jù)的指針。
(int)Function表示將函數(shù)的入口地址強(qiáng)制轉(zhuǎn)換成int類(lèi)型的數(shù)據(jù)。
分析到這里,相信你已經(jīng)明白*(int*)&p=(int)Function;表示將函數(shù)的入口地址賦值給指針變量p。
那么(*p) ();就是表示對(duì)函數(shù)的調(diào)用。
講解到這里,相信你已經(jīng)明白了。其實(shí)函數(shù)指針與普通指針沒(méi)什么差別,只是指向的內(nèi)容不同而已。
使用函數(shù)指針的好處在于,可以將實(shí)現(xiàn)同一功能的多個(gè)模塊統(tǒng)一起來(lái)標(biāo)識(shí),這樣一來(lái)更容易后期的維護(hù),系統(tǒng)結(jié)構(gòu)更加清晰?;蛘邭w納為:便于分層設(shè)計(jì)、利于系統(tǒng)抽象、降低耦合度以及使接口與實(shí)現(xiàn)分開(kāi)。
4.(*(void(*) ())0)()------這是什么?
是不是感覺(jué)上面的例子太簡(jiǎn)單,不夠刺激?好,那就來(lái)點(diǎn)刺激的,看下面這個(gè)例子:
(*(void(*) ())0)();
這是《C Traps and Pitfalls》這本經(jīng)典的書(shū)中的一個(gè)例子。沒(méi)有發(fā)狂吧?下面我們就來(lái)分析分析:
第一步:void(*) (),可以明白這是一個(gè)函數(shù)指針類(lèi)型。這個(gè)函數(shù)沒(méi)有參數(shù),沒(méi)有返回值。
第二步:(void(*) ())0,這是將0強(qiáng)制轉(zhuǎn)換為函數(shù)指針類(lèi)型,0是一個(gè)地址,也就是說(shuō)一個(gè)函數(shù)存在首地址為0的一段區(qū)域內(nèi)。
第三步:(*(void(*) ())0),這是取0地址開(kāi)始的一段內(nèi)存里面的內(nèi)容,其內(nèi)容就是保存在首地址為0的一段區(qū)域內(nèi)的函數(shù)。
第四步:(*(void(*) ())0)(),這是函數(shù)調(diào)用。
好像還是很簡(jiǎn)單是吧,上面的例子再改寫(xiě)改寫(xiě):
(*(char**(*) (char **,char **))0) ( char **,char **);
如果沒(méi)有上面的分析,肯怕不容易把這個(gè)表達(dá)式看明白吧。不過(guò)現(xiàn)在應(yīng)該是很簡(jiǎn)單的一件事了。讀者以為呢?
5.函數(shù)指針數(shù)組
現(xiàn)在我們清楚表達(dá)式
char * (*pf)(char * p);
定義的是一個(gè)函數(shù)指針pf。既然pf是一個(gè)指針,那就可以儲(chǔ)存在一個(gè)數(shù)組里。把上式修改一下:
char * (*pf[3])(char * p);
這是定義一個(gè)函數(shù)指針數(shù)組。
它是一個(gè)數(shù)組,數(shù)組名為pf,數(shù)組內(nèi)存儲(chǔ)了3個(gè)指向函數(shù)的指針。這些指針指向一些返回值類(lèi)型為指向字符的指針、參數(shù)為一個(gè)指向字符的指針的函數(shù)。
這念起來(lái)似乎有點(diǎn)拗口。不過(guò)不要緊,關(guān)鍵是你明白這是一個(gè)指針數(shù)組,是數(shù)組。函數(shù)指針數(shù)組怎么使用呢?這里也給出一個(gè)非常簡(jiǎn)單的例子,只要真正掌握了使用方法,再?gòu)?fù)雜的問(wèn)題都可以應(yīng)對(duì)。
如下:
#include <stdio.h> #include <string.h> <br>char * fun1(char * p) { printf("%s\n",p); return p; } char * fun2(char * p) { printf("%s\n",p); return p; } char * fun3(char * p) { printf("%s\n",p); return p; } <br>int main() { char * (*pf[3])(char * p); pf[0] = fun1; //可以直接用函數(shù)名 pf[1] = &fun2; //可以用函數(shù)名加上取地址符 pf[2] = &fun3;<br> pf[0]("fun1"); pf[0]("fun2"); pf[0]("fun3"); return 0; }
6.函數(shù)指針數(shù)組的指針
看著這個(gè)標(biāo)題沒(méi)發(fā)狂吧?函數(shù)指針就夠一般初學(xué)者折騰了,函數(shù)指針數(shù)組就更加麻煩,現(xiàn)在的函數(shù)指針數(shù)組指針就更難理解了。
其實(shí),沒(méi)這么復(fù)雜。前面詳細(xì)討論過(guò)數(shù)組指針的問(wèn)題,這里的函數(shù)指針數(shù)組指針不就是一個(gè)指針嘛。只不過(guò)這個(gè)指針指向一個(gè)數(shù)組,這個(gè)數(shù)組里面存的都是指向函數(shù)的指針。僅此而已。
下面就定義一個(gè)簡(jiǎn)單的函數(shù)指針數(shù)組指針:
char * (*(*pf)[3])(char * p);
注意,這里的pf和上一節(jié)的pf就完全是兩碼事了。上一節(jié)的pf并非指針,而是一個(gè)數(shù)組名;這里的pf確實(shí)是實(shí)實(shí)在在的指針。這個(gè)指針指向一個(gè)包含了3個(gè)元素的數(shù)組;這個(gè)數(shù)字里面存的是指向函數(shù)的指針;這些指針指向一些返回值類(lèi)型為指向字符的指針、參數(shù)為一個(gè)指向字符的指針的函數(shù)。
這比上一節(jié)的函數(shù)指針數(shù)組更拗口。其實(shí)你不用管這么多,明白這是一個(gè)指針就ok了。其用法與前面講的數(shù)組指針沒(méi)有差別。下面列一個(gè)簡(jiǎn)單的例子:
#include <stdio.h> #include <string.h> char * fun1(char * p) { printf("%s\n",p); return p; } char * fun2(char * p) { printf("%s\n",p); return p; } char * fun3(char * p) { printf("%s\n",p); return p; } int main() { char * (*a[3])(char * p); char * (*(*pf)[3])(char * p); pf = &a; a[0] = fun1; a[1] = &fun2; a[2] = &fun3; pf[0][0]("fun1"); pf[0][1]("fun2"); pf[0][2]("fun3"); return 0; }
相關(guān)文章
Qt使用QCamera實(shí)現(xiàn)切換相機(jī),分辨率和圖像捕獲功能
這篇文章主要為大家介紹了如何利用Qt中的相機(jī)類(lèi)QCamera,取景器類(lèi)QCameraViewfinder,圖像捕獲類(lèi)QCameraImageCapture實(shí)現(xiàn)切換相機(jī)、分辨率和圖像捕獲功能,需要的可以了解一下2023-04-04Vscode搭建遠(yuǎn)程c開(kāi)發(fā)環(huán)境的圖文教程
很久沒(méi)有寫(xiě)C語(yǔ)言了,今天抽空學(xué)習(xí)下C語(yǔ)言知識(shí),接下來(lái)通過(guò)本文給大家介紹Vscode搭建遠(yuǎn)程c開(kāi)發(fā)環(huán)境的詳細(xì)步驟,本文通過(guò)圖文實(shí)例代碼相結(jié)合給大家介紹的非常詳細(xì),需要的朋友參考下吧2021-11-11利用Qt實(shí)現(xiàn)獲取計(jì)算機(jī)的硬件信息
在開(kāi)發(fā)時(shí),常常會(huì)需要用到計(jì)算機(jī)的相關(guān)信息。利用這些信息,我們可以開(kāi)發(fā)一些輔助模塊。本文將利用Qt實(shí)現(xiàn)獲取計(jì)算機(jī)的硬件信息,感興趣的可以嘗試一下2022-12-12C語(yǔ)言代碼實(shí)現(xiàn)猜數(shù)字
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言代碼實(shí)現(xiàn)猜數(shù)字,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11C語(yǔ)言多線程服務(wù)器的實(shí)現(xiàn)實(shí)例
這篇文章主要介紹了C語(yǔ)言多線程服務(wù)器的實(shí)現(xiàn)實(shí)例,文章用實(shí)例講解的很清楚,有對(duì)這方面不太懂的同學(xué)可以參考下2021-02-02C++ 數(shù)據(jù)結(jié)構(gòu)鏈表的實(shí)現(xiàn)代碼
這篇文章主要介紹了C++ 數(shù)據(jù)結(jié)構(gòu)鏈表的實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-01-01C語(yǔ)言簡(jiǎn)單實(shí)現(xiàn)三子棋游戲
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言簡(jiǎn)單實(shí)現(xiàn)三子棋游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08推薦幾款C/C++的編譯器、編譯環(huán)境(非常全面的比較)
這篇文章主要介紹了C/C++編譯器的一些易混淆概念,這里腳本之家小編特為大家分享一下,需要的朋友可以參考下2021-06-06