函數(shù)指針就是函數(shù)的指針。它是一個(gè)指針,指向一個(gè)函數(shù)。(即函數(shù)在內(nèi)存中的起始位置地址)
實(shí)際上,所有的函數(shù)名在表達(dá)式和初始化中,總是隱式地退化為指針。
例:
int r , (*fp)( ) , func( ) ;
fp= func ; //函數(shù)名退化為指針
r= (*fp)( ) ; //等價(jià)于r=fp( ) ;
無(wú)論fp是函數(shù)名還是函數(shù)指針,都能正確工作。因?yàn)楹瘮?shù)總是通過(guò)指針進(jìn)行調(diào)用的!
--
例:
int f(int) ; //函數(shù)聲明
int (*fp)(int) = &f ;//此取地址符是可選的。編譯器就把函數(shù)名當(dāng)做函數(shù)的入口地址。
int ans ;
//以下三種方式可調(diào)用函數(shù)
ans= f(25) ;
ans= (*fp)(25) ;
ans= fp(25) ;
函數(shù)名就是一個(gè)函數(shù)指針常量,函數(shù)調(diào)用操作符(即一對(duì)括號(hào))相當(dāng)于解引用。
函數(shù)的執(zhí)行過(guò)程:
函數(shù)名首先被轉(zhuǎn)換為一個(gè)函數(shù)指針常量,該指針指定函數(shù)在內(nèi)存中的位置。然后函數(shù)調(diào)用操作符調(diào)用該函數(shù),執(zhí)行開(kāi)始于這個(gè)地址的代碼。
void fun() { printf("Call fun "); }
int main(void)
{
void(*p)( ) ;
*(int*)&p = (int)fun ;
(*p)() ;
return 0 ;
}
指針的強(qiáng)制類型轉(zhuǎn)換只不過(guò)是改變了編譯器對(duì)二進(jìn)制位的解釋方法罷了。
*(int *)&p = (int)fun ;中的fun是一個(gè)函數(shù)地址,被強(qiáng)制轉(zhuǎn)換為int數(shù)字。 左邊的(int *)&p是把函數(shù)指針p轉(zhuǎn)換為int型指針。
*(int *)&p = (int)fun ;表示將函數(shù)的入口地址賦值給指針變量p。
(*p)( ) ;表示對(duì)函數(shù)的調(diào)用。
1,轉(zhuǎn)移表(轉(zhuǎn)移表就是一個(gè)函數(shù)指針數(shù)組)
即可用來(lái)實(shí)現(xiàn)“菜單驅(qū)動(dòng)系統(tǒng)”。系統(tǒng)提示用戶從菜單中選擇一個(gè)選項(xiàng),每個(gè)選項(xiàng)由不同的函數(shù)提供服務(wù)。
【若每個(gè)選項(xiàng)包含許多操作,用switch操作,會(huì)使程序變得很長(zhǎng),可讀性差。這時(shí)可用轉(zhuǎn)移表的方式】
例:
void(*f[3])(int) = {function1, function2, function3} ; //定義一個(gè)轉(zhuǎn)移表
(*f[choice])( ) ; //根據(jù)用戶的選擇來(lái)調(diào)用相應(yīng)的函數(shù)
2,回調(diào)函數(shù)
(用函數(shù)指針做形參,用戶根據(jù)自己的環(huán)境寫(xiě)個(gè)簡(jiǎn)單的函數(shù)模塊,傳給回調(diào)函數(shù),這樣回調(diào)函數(shù)就能在不同的環(huán)境下運(yùn)行了,提高了模塊的復(fù)用性)
【回調(diào)函數(shù)實(shí)現(xiàn)與環(huán)境無(wú)關(guān)的核心操作,而把與環(huán)境有關(guān)的簡(jiǎn)單操作留給用戶完成,在實(shí)際運(yùn)行時(shí)回調(diào)函數(shù)通過(guò)函數(shù)指針調(diào)用用戶的函數(shù),這樣其就能適應(yīng)多種用戶需求】
例:C庫(kù)函數(shù)中的快速排序函數(shù)
void qsort(void *base, int nelem, size_t width, int (*fcmp)(void*, void*) );//fcmp為函數(shù)指針。
這樣,由用戶實(shí)現(xiàn)fcmp的比較功能(用戶可根據(jù)需要,寫(xiě)整型值的比較、浮點(diǎn)值的比較,字符串的比較 等)這樣qsort函數(shù)就能適應(yīng)各種不同的類型值的排序。
使用函數(shù)指針的好處在于:
可以將實(shí)現(xiàn)同一功能的多個(gè)模塊統(tǒng)一起來(lái)標(biāo)識(shí),這樣一來(lái)更容易后期維護(hù),系統(tǒng)結(jié)構(gòu)更加清晰。或者歸納為:便于分層設(shè)計(jì)、利于系統(tǒng)抽象、降低耦合度以及使接口與實(shí)現(xiàn)分開(kāi)。
函數(shù)指針數(shù)組的指針
例:char *(*(pf)[3])(char )
這個(gè)指針指向一個(gè)數(shù)組,這個(gè)數(shù)組里存儲(chǔ)的都是指向函數(shù)的指針。它們指向的是一種返回值為字符指針,參數(shù)為字符指針的函數(shù)。
從外到內(nèi),層層剝開(kāi),先找核心,再向右看
找到核心變量后,從右向左讀。
簡(jiǎn)單的例子:
int *f() ; // f: 返回指向int型的指針
步驟:
1)找標(biāo)識(shí)符f:讀作”f是…”
2)向右看,發(fā)現(xiàn)”()”讀作”f是返回…的函數(shù)”
3)向右看沒(méi)有什么,向左看,發(fā)現(xiàn)*,讀作”f是返回指向…的指針的函數(shù)”
4)繼續(xù)向左看,發(fā)現(xiàn)int,讀作”f是返回指向int型的指針的函數(shù)”int (pf)() ; // pf是一個(gè)指針——指向返回值為int型的函數(shù)
1)標(biāo)識(shí)符pf,讀作“pf是…”
2)向右看,發(fā)現(xiàn)),向左看,發(fā)現(xiàn)\,讀作 “pf是指向…的指針”3)向右看,發(fā)現(xiàn)”()”,讀作“pf是指向返回…的函數(shù)的指針”4)向右看,沒(méi)有,向左看發(fā)現(xiàn)int,讀作”pf是指向返回int型的函數(shù)的指針
復(fù)雜指針的舉例:void (*b[10]) (void (*)());
首先找到核心:b是一個(gè)數(shù)組,這個(gè)數(shù)組有10個(gè)元素,每一個(gè)元素都是一個(gè)指針,指針指向一個(gè)函數(shù),函數(shù)參數(shù)是“void(*)()”【 這個(gè)參數(shù)又是一個(gè)指針,指向一個(gè)函數(shù),函數(shù)參數(shù)為空,返回值是“void”】 返回值是“void”。完畢!
“建立一個(gè)類型別名的方法很簡(jiǎn)單,在傳統(tǒng)的變量聲明表達(dá)式里用類型名替代變量名,然后把關(guān)鍵字typedef加在該語(yǔ)句的開(kāi)頭”。
舉例:
1、
void (*b[10]) (void (*)());
typedef void (*pfv)(); //先把上式的后半部分用typedef換掉
typedef void (*pf_taking_pfv)(pfv); //再把前半部分用typedef換掉
pf_taking_pfv b[10]; //整個(gè)用typedef換掉
跟void (*b[10]) (void (*)());的效果一樣!
2、
doube(*)() (*pa)[9];
typedef double(*PF)(); //先替換前半部分
typedef PF (*PA)[9]; //再替換后半部分
PA pa;
跟doube(*)() (*pa)[9];的效果一樣!
1、我們?yōu)槭裁葱枰羔槪?/strong>
因?yàn)槲覀円L問(wèn)一個(gè)對(duì)象,我們要改變一個(gè)對(duì)象。要訪問(wèn)一個(gè)對(duì)象,必須先知道它在哪,也就是它在內(nèi)存中的地址。地址就是指針值。
所以我們有
函數(shù)指針:某塊函數(shù)代碼的起始位置(地址)
指針的指針:因?yàn)槲乙L問(wèn)(或改變)某個(gè)變量,只是這個(gè)變量是指針罷了
2、為什么要有指針類型?
因?yàn)槲覀冊(cè)L問(wèn)的對(duì)象一般占據(jù)多個(gè)字節(jié),而代表它們的地址值只是其中最低字節(jié)的地址,我們要完整的訪問(wèn)對(duì)象,必須知道它們總共占據(jù)了多少字節(jié)。而指針類型即向我們提供這樣的信息。
注意:一個(gè)指針變量向我們提供了三種信息:
①一個(gè)首字節(jié)的地址值(起始位置)
②這個(gè)指針的作用范圍(步長(zhǎng))
③對(duì)這個(gè)范圍中的數(shù)位的解釋規(guī)則(解碼規(guī)則)
【編譯器就像一個(gè)以步數(shù)測(cè)量距離的盲人。故你要告訴它從哪開(kāi)始走,走多少步,并且告訴他如何理解這里面的信息】
3、強(qiáng)制類型轉(zhuǎn)換的真相?
學(xué)過(guò)匯編的人都知道,什么指針,什么char,int,double,什么數(shù)組指針,函數(shù)指針,指針的指針,在內(nèi)存中都是一串二進(jìn)制數(shù)罷了。只是我們賦予了這些二進(jìn)制數(shù)不同的含義,給它們?cè)O(shè)定一些不同的解釋規(guī)則,讓它們代表不同的事物。
(比如1000 0000 0000 0001 是內(nèi)存中某4個(gè)字節(jié)中的內(nèi)容,如果我們認(rèn)為它是int型,則按int型的規(guī)則解釋它為-2^31+ 1;如果我們認(rèn)為它是unsigned int ,則被解釋為2^31+ 1;當(dāng)然我們也可把它解釋為一個(gè)地址值,數(shù)組的地址,函數(shù)的地址,指針的地址等)
如果我們使用匯編編程,我們必須根據(jù)上下文需要,用大腦記住這個(gè)值當(dāng)前的代表含義,當(dāng)程序中有很多這樣的值時(shí),我們必須分別記清它們當(dāng)前代表的含義。這樣極易導(dǎo)致誤用,所以編譯器出現(xiàn)了,讓它來(lái)幫我們記住這些值當(dāng)前表示的含義。
當(dāng)我們想讓某個(gè)值換一種解釋的方案時(shí),就用強(qiáng)制類型轉(zhuǎn)換的方式來(lái)告訴編譯器,編譯器則修改解釋它的規(guī)則,而內(nèi)存中的二進(jìn)制數(shù)位是不變的(涉及浮點(diǎn)型的強(qiáng)制轉(zhuǎn)換除外,它們是舍掉一些位,保留一些位)。
更多建議: