腳本之家 你與百萬(wàn)開(kāi)發(fā)者在一起
前言函數(shù)指針是什么?如何使用函數(shù)指針?函數(shù)指針到底有什么大用?本文將一一介紹。 如何理解函數(shù)指針如果有int *類(lèi)型變量,它存儲(chǔ)的是int類(lèi)型變量的地址;那么對(duì)于函數(shù)指針來(lái)說(shuō),它存儲(chǔ)的就是函數(shù)的地址。函數(shù)也是有地址的,函數(shù)實(shí)際上由載入內(nèi)存的一些指令組成,而指向函數(shù)的指針存儲(chǔ)了函數(shù)指令的起始地址。如此看來(lái),函數(shù)指針并沒(méi)有什么特別的。我們可以查看程序中函數(shù)的地址: #include <stdio.h> 編譯:
查看test函數(shù)相對(duì)地址(并非實(shí)際運(yùn)行時(shí)的地址): $ nm testFun |grep test #查看test函數(shù)的符號(hào)表信息 如何聲明函數(shù)指針聲明普通類(lèi)型指針時(shí),需要指明指針?biāo)赶虻臄?shù)據(jù)類(lèi)型,而聲明函數(shù)指針時(shí),也就要指明指針?biāo)赶虻暮瘮?shù)類(lèi)型,即需要指明函數(shù)的返回類(lèi)型和形參類(lèi)型。例如對(duì)于下面的函數(shù)原型:
它是一個(gè)返回值為int類(lèi)型,參數(shù)是兩個(gè)int類(lèi)型的函數(shù),那么如何聲明該類(lèi)型函數(shù)的指針呢?很簡(jiǎn)單,將函數(shù)名替換成(*pf)形式即可,即我們把sum替換成(*fp)即可,fp為函數(shù)指針名,結(jié)果如下: int (*fp)(int,int); 這樣就聲明了和sum函數(shù)類(lèi)型相同的函數(shù)指針fp。這里說(shuō)明兩點(diǎn),第一,*和fp為一體,說(shuō)明了fp為指針類(lèi)型,第二,*fp需要用括號(hào)括起來(lái),否則就會(huì)變成下面的情況:
這種情況下,意思就大相徑庭了,它聲明了一個(gè)參數(shù)為兩個(gè)int類(lèi)型,返回值為int類(lèi)型的指針的函數(shù),而不再是一個(gè)函數(shù)指針了。 在經(jīng)常使用函數(shù)指針之后,我們很快就會(huì)發(fā)現(xiàn),每次聲明函數(shù)指針都要帶上長(zhǎng)長(zhǎng)的形參和返回值,非常不便。這個(gè)時(shí)候,我們應(yīng)該想到使用typedef,即為某類(lèi)型的函數(shù)指針起一個(gè)別名,使用起來(lái)就方便許多了。例如,對(duì)于前面提到的函數(shù)可以使用下面的方式聲明: typedef int (*myFun)(int,int);//為該函數(shù)指針類(lèi)型起一個(gè)新的名字 上面的myFun就是一個(gè)函數(shù)指針類(lèi)型,在其他地方就可以很方便地用來(lái)聲明變量了。typedef的使用不在本文的討論范圍,但是特別強(qiáng)調(diào)一句,typedef中聲明的類(lèi)型在變量名的位置出現(xiàn),理解了這一句,也就很容易使用typedef了。因而下面的方式是錯(cuò)誤的:
為函數(shù)指針賦值賦值也很簡(jiǎn)單,既然是指針,將對(duì)應(yīng)指針類(lèi)型賦給它既可。例如: #include<stdio.h> 在這里,聲明了返回類(lèi)型為int,接受兩個(gè)int類(lèi)型參數(shù)的函數(shù)指針f1和f2,分別給它們進(jìn)行了賦值。表達(dá)式1和表達(dá)式2在作用上并沒(méi)有什么區(qū)別。因?yàn)楹瘮?shù)名在被使用時(shí)總是由編譯器把它轉(zhuǎn)換為函數(shù)指針,而前面加上&不過(guò)顯式的說(shuō)明了這一點(diǎn)罷了。 調(diào)用調(diào)用也很容易,把它看成一個(gè)普通的函數(shù)名即可:
在函數(shù)指針后面加括號(hào),并傳入?yún)?shù)即可調(diào)用,其中表達(dá)式1和表達(dá)式2似乎都可以成功調(diào)用,但是哪個(gè)是正確的呢?ANSI C認(rèn)為這兩種形式等價(jià)。 函數(shù)指針有何用函數(shù)指針的應(yīng)用場(chǎng)景比較多,以庫(kù)函數(shù)qsort排序函數(shù)為例,它的原型如下: void qsort(void *base,size_t nmemb,size_t size , int(*compar)(const void *,const void *)); 看起來(lái)很復(fù)雜對(duì)不對(duì)?拆開(kāi)來(lái)看如下:
拿掉第四個(gè)參數(shù)后,很容易理解,它是一個(gè)無(wú)返回值的函數(shù),接受4個(gè)參數(shù),第一個(gè)是void*類(lèi)型,代表原始數(shù)組,第二個(gè)是size_t類(lèi)型,代表數(shù)據(jù)數(shù)量,第三個(gè)是size_t類(lèi)型,代表單個(gè)數(shù)據(jù)占用空間大小,而第四個(gè)參數(shù)是函數(shù)指針。這第四個(gè)參數(shù),即函數(shù)指針指向的是什么類(lèi)型呢? int(*compar)(const void *,const void *) 很顯然,這是一個(gè)接受兩個(gè)const void*類(lèi)型入?yún)?,返回值為int的函數(shù)指針。 在這里函數(shù)指針作為了參數(shù),而他同樣可以作為返回值,創(chuàng)建數(shù)組,作為結(jié)構(gòu)體成員變量等等,它們的具體應(yīng)用我們?cè)诤竺娴奈恼轮袝?huì)介紹,本文不作展開(kāi)。本文只介紹一個(gè)簡(jiǎn)單實(shí)例。 實(shí)例介紹我們通過(guò)一個(gè)實(shí)例來(lái)看函數(shù)指針怎么使用。假設(shè)有一學(xué)生信息,需要按照學(xué)生成績(jī)進(jìn)行排序,該如何處理呢?
我們創(chuàng)建了一個(gè)學(xué)生信息結(jié)構(gòu),結(jié)構(gòu)成員包括名字,學(xué)號(hào)和成績(jī)。main函數(shù)中創(chuàng)建了一個(gè)包含三個(gè)學(xué)生信息的數(shù)組,并使用qsort函數(shù)對(duì)數(shù)組按照學(xué)生成績(jī)進(jìn)行排序。qsort函數(shù)第四個(gè)參數(shù)是函數(shù)指針,因此我們需要傳入一個(gè)函數(shù)指針,并且這個(gè)函數(shù)指針的入?yún)⑹莄ont void *類(lèi)型,返回值為int。我們通過(guò)前面的學(xué)習(xí)知道了函數(shù)名本身就是指針,因此只需要將我們自己實(shí)現(xiàn)的studentCompare作為參數(shù)傳入即可。 最終運(yùn)行結(jié)果如下: name:two,id:2,score:77 可以看到,最終學(xué)生信息按照分?jǐn)?shù)從低到高輸出。 總結(jié)本文介紹了函數(shù)指針的聲明和使用。更多使用將在后面的文章介紹,本文總結(jié)如下:
思考下面的聲明如何理解
|
|
來(lái)自: 長(zhǎng)沙7喜 > 《編程》