請(qǐng)問(wèn)下面這條語(yǔ)句是什么意思?
void (*b[10]) (void (*)())
如果你可以看懂這條語(yǔ)句的含義,那可以忽略此文~
C變量的聲明都是由兩部分組成的:類(lèi)型和一組類(lèi)似表達(dá)式的聲明符(declarator)。聲明符類(lèi)似于表達(dá)式,對(duì)它求值應(yīng)該返回一個(gè)聲明中給定類(lèi)型的結(jié)果。例如,我們來(lái)看一個(gè)簡(jiǎn)單的聲明:
float f;
這里f就是聲明符,對(duì)其求值,應(yīng)該得到一個(gè)float型的數(shù)值。
然后看括號(hào)的作用,比如:
float ((f));
很簡(jiǎn)單,這個(gè)聲明的含義就是,((f))的類(lèi)型為浮點(diǎn)類(lèi)型。因?yàn)槔ㄌ?hào)和f之間沒(méi)有其他的修飾了,所以,我們可以推知,f也是浮點(diǎn)型。
再來(lái)看稍微復(fù)雜點(diǎn)的:
float f();
我們注意到有一個(gè)()表達(dá)式出現(xiàn),這個(gè)聲明符的含義是一個(gè)函數(shù)。所以這個(gè)聲明的含義就是,f()的求值結(jié)果是一個(gè)浮點(diǎn)數(shù),也就是說(shuō),f是個(gè)返回值為浮點(diǎn)數(shù)的函數(shù)。
OK,這些都很簡(jiǎn)單,再看一個(gè):
float *f();
這個(gè)就是表示*f()是個(gè)浮點(diǎn)表達(dá)式,而()的結(jié)合優(yōu)先級(jí)高于*,所以,*f()也就是*(f()),f是個(gè)函數(shù),返回值是個(gè)指針,這個(gè)指針指向一個(gè)浮點(diǎn)數(shù)。
如果*f是先結(jié)合的呢?比如:
float (*f)();
這時(shí),f就不是和()結(jié)合而成為一個(gè)函數(shù)名,而是和*結(jié)合成為一個(gè)指針名,這個(gè)指針,是指向函數(shù)入口的函數(shù)指針,而這個(gè)函數(shù),返回值是浮點(diǎn)型。
現(xiàn)在我們知道怎么聲明一個(gè)指定類(lèi)型的變量了。在這個(gè)聲明表達(dá)式中,把變量名,和聲明末尾的分號(hào)去掉,剩余的部分用一個(gè)括號(hào)整個(gè)括起來(lái),這樣就得到了這個(gè)變量的類(lèi)型聲明了,比如:
float (*f)();
這個(gè)表示f是一個(gè)指向返回值為浮點(diǎn)型的函數(shù)指針。而我們把f這個(gè)變量名,和最后的;號(hào)去掉,就得到:
( float (*)() )
這個(gè)就表示,這個(gè)表達(dá)式是一個(gè)類(lèi)型,即“指向返回值為浮點(diǎn)型的函數(shù)的指針”。如果用這個(gè)類(lèi)型去修飾一個(gè)變量名,我們就叫它類(lèi)型轉(zhuǎn)換符。
void (*b) ();???????? ---->b是指向參數(shù)為空返回值類(lèi)型為void的函數(shù)指針
void (*b[10]) ();?? ---->b是一個(gè)有10個(gè)指針元素的數(shù)組,每個(gè)指針指向參數(shù)為空返回值類(lèi)型為void的函數(shù)
現(xiàn)在有了這些預(yù)備知識(shí),我們可以回頭看標(biāo)題的聲明到底是什么意思了:
void (*b[10]) (void (*)());
首先,表達(dá)式的后半部分被兩個(gè)()分隔開(kāi)了,我們分別分析它們。( *b[10] ),其中出現(xiàn)了變量名b,很容易就知道,b是一個(gè)有10個(gè)元素的數(shù)組,每個(gè)元素都是一個(gè)指針。
然后,看(void(*)()),其中沒(méi)有出現(xiàn)變量名,所以它代表了一個(gè)類(lèi)型,即“指向返回值為void型的函數(shù)的指針“,而我們知道,C語(yǔ)法中,類(lèi)型修飾符是必須出現(xiàn)在變量名的左邊的,而在整個(gè)表達(dá)式中這個(gè)類(lèi)型符是在變量名b的右邊,所以, (void(*)())最外層的這個(gè)(),表示了定義了一個(gè)函數(shù),這個(gè)函數(shù)有一個(gè)參數(shù),就是一個(gè)指針,具體來(lái)說(shuō),就是“指向返回值為void型的函數(shù)的指針“。
這樣就很清楚了,b數(shù)組里,每一個(gè)指針元素,都是一個(gè)函數(shù)指針,這個(gè)函數(shù)有一個(gè)參數(shù),這個(gè)參數(shù)是一個(gè)函數(shù)指針。整個(gè)表達(dá)式最左邊的void,則定義了b數(shù)組中函數(shù)指針?biāo)赶蚝瘮?shù)的返回值類(lèi)型。
在一串繞口令式的解說(shuō)后,我們終于看到了事實(shí)的真相:這行代碼就是逗你玩...
如果不是故意偷懶,這樣代碼的作者真是在玩弄閱讀者的感情,如果非要實(shí)現(xiàn)這樣一個(gè)復(fù)雜的定義的話(huà),我們也是有更好的方法的,就是使用typedef來(lái)進(jìn)行類(lèi)型聲明。
在面對(duì)void (*b[10]) (void (*)());時(shí),我們可以先聲明后半部分的類(lèi)型:
typdef void (*pFunParam)();
即表示,類(lèi)型pFunParam,是一個(gè)函數(shù)指針。
然后,針對(duì)整個(gè)表達(dá)式聲明一個(gè)類(lèi)型:
typedef void (*pFun)(pFunParam);
即表示,類(lèi)型pFun,是一個(gè)函數(shù)指針。此函數(shù)的參數(shù)類(lèi)型為pFunParam。
最后,進(jìn)行變量的聲明:
pFun b[10];
這樣,就清晰許多了吧。最重要的時(shí),利用這樣的方式,將編寫(xiě)者的設(shè)計(jì)思路也體現(xiàn)在了代碼中。
最后,介紹一個(gè)更快速的閱讀方法:從右向左。
編譯器在進(jìn)行代碼掃描時(shí)是從左向右的,而我們?cè)诮庾x這個(gè)表達(dá)式的時(shí)候,從右向左的方法會(huì)更容易些,如:
從右向左掃描以上表達(dá)式,首先我們看到一個(gè)“)”,因?yàn)槠溆疫呍贈(zèng)]有變量,所以我們知道這是一個(gè)函數(shù)定義,然后,又看到一對(duì)“()“,顯然也是函數(shù)定義,在就是(*),顯然和后面的()聯(lián)合起來(lái)表達(dá)了一個(gè)函數(shù)指針,再左邊的void,即修飾了此指針的類(lèi)型;然后出現(xiàn)了和最右邊”)"對(duì)應(yīng)的”(”,顯然這個(gè)函數(shù)定義也完成了,再左邊的[10]表達(dá)了一個(gè)數(shù)組,左邊是它的名字b,而更左邊的“*”和后面的“(“一起表達(dá)了一個(gè)函數(shù)指針,這個(gè)函數(shù)指針定義了b數(shù)組中元素的類(lèi)型。再往左,指針元素的定義void也出現(xiàn)了。這樣,我們就又看到了此表達(dá)式的真相。