詳解C C 函式指標宣告

2021-07-28 02:11:57 字數 4167 閱讀 9311

**: 

要理解乙個c程式,僅僅理解組成該程式的符號是不夠的。程式設計師還必須理解這些符號是如何組合成宣告、表示式、語句和程式的。

我們先來看看下面的乙個語句:

1

( *(void(*)())0)();

這是當計算機啟動時,硬體將呼叫首位址為0位置的子例程。像這樣的表示式恐怕會令每個c/c++程式設計師的內心都「不寒而慄」吧。

然而,完全不用害怕,任何c變數的宣告都是由兩部分組成:型別以及一組類似表示式的宣告符。最簡單的宣告變數,如:

1

floatf , g ;

這個宣告的含義是:當對其求值時,表示式f和g的型別為浮點型。

同樣的邏輯也適用於函式和指標型別的宣告,例如:

1

floatff();

這個宣告的含義是:表示式ff()求值結果是乙個浮點數,也就是說,ff是乙個返回值為浮點型別的函式,類似地:

1

float*pf;

這個宣告的含義是*pf是乙個浮點數,也就是說,pf是乙個指向浮點數的指標。

以上這些形式在宣告中還可以組合起來,就像在表示式中進行組合一樣,因此:

1

float*g() , (*h)();

表示*g()與(*h)()是浮點表示式。因為()結合優先順序高於*,*g()也就是*(g()):g是乙個函式,該函式的返回值型別為指向浮點數的指標。同理,可以得出h是乙個函式指標,h所指向函式的返回值為浮點型別。

一旦我們知道了如何宣告乙個給定型別的變數,那麼該型別的型別轉換符就很容易得到了:只需要把宣告中的變數名和宣告末尾的分號去掉,再將剩餘的部分用乙個括號整個「封裝」起來即可。例如:

1

float(*h)();

表示h是乙個指向返回值為浮點型別的函式的指標,因此,

1

(float(*)())

表示乙個「指向返回值為浮點型別的函式的指標」的型別轉換符。

那麼,我們現在來看看前面我們提出的表示式:

1

( *(void(*)())0)();

第一步,假定變數fp是乙個函式指標,那麼如何呼叫fp所指向的函式呢?呼叫方法如下:

1

(*fp)();

因為fp是乙個函式指標,那麼*fp就是該指標所指向的函式,所以(*fp)()就是呼叫該函式的方式。

表示式(*fp)()中,*fp兩側的括號非常重要,因為函式運算子()的優先順序高於單目運算子*。如果*fp兩側沒有括號,那麼*fp()實際上與*(fp())的含義完全一致。

現在剩下的問題就只是找到乙個恰到的表示式來替換fp。我們將在分析的第二步來解決這個問題。如果c編譯器能夠理解我們大腦中對於型別的認識,那麼我們可以這樣寫:

1

(*0)()

上式並不能生效,因為運算子*必須要乙個指標來做運算元。而且這個指標還應該是乙個函式指標,這樣經運算子*作用後的結果才能作為函式被呼叫。因此,在上式中必須對0作型別轉換,轉換後的型別可以大致描述為:「指向返回值為void型別的函式的指標」。

如果fp是乙個指向返回值為void型別的函式的指標,那麼(*fp)()的值為void,fp的宣告如下:

1

viod (*fp)();

因此,將常數0轉型為「指向返回值為void的函式的指標」型別,可以這樣寫:

1

(void(*)())0

因此,我們可以用(void(*)())0來替換fp,從而得到:

1

( *(void(*)())0)();

當然,我們用typedef來解決這個問題能夠表述更加清晰:

1

2

typedefvoid(*fp)();

(*(fp)0)();

這個問題就可以解決了。

我們再來考慮signal庫函式,一般情況下,程式設計師並不主動宣告signal函式,而是直接使用標頭檔案signal.h中的宣告。那麼,在標頭檔案signal.h中,signal函式是如何宣告的呢?

首先,讓我們從使用者定義的訊號處理函式開始考慮,這無疑是最容易解決的。該函式可以定義如下:

1

2

3

voidsigfunc(intn)

函式sigfunc的引數是乙個代表特定訊號的整數值,此處我們暫時忽略它。

上面假設的函式體定義了sigfunc函式,因而sigfunc函式的宣告可以如下:

1

voidsigfunc(int);

現在假定我們希望宣告乙個指向sigfunc函式的指標變數,不妨命名為sfp。因而sfp指向sigfunc函式,*sfp就代表sigfunc函式,因此*sfp可以被呼叫。因此我們可以如下這樣宣告sfp:

1

void(*sfp)(int);

因為signal函式的返回值型別與sfp的返回值型別一樣,上式也就宣告了signal函式,我們不妨可以如下宣告signal函式:

1

void(*signal(something))(int);

此處的something代表了signal函式的引數型別,我們還需要進一步了解如何宣告它們。上面宣告可以這樣理解:傳遞適當的引數以呼叫signal函式,對signal函式返回值(為函式指標型別)解除引用,然後傳遞乙個整型引數呼叫解除引用後所得函式,最後返回值為void型別。因此,signal函式的返回值是乙個指向返回值為void型別的函式指標。

那麼,signal函式的引數又是如何呢?,signal函式接受兩個引數:乙個整型的訊號編號,以及乙個指向使用者定義的訊號處理函式的指標。我們此前一定定義了指向使用者定義的訊號處理函式的指標sfp:

1

void(*sfp)(int);

sfp的型別可以通過將上面的宣告中的sfp去掉而得到,即 void(*)(int)。此外,signal函式的返回值是乙個指向呼叫前的使用者定義訊號處理函式的指標,這個指標的型別與sfp指標型別一致。因此我們可以如下宣告signal函式:

1

void(*signal(int,void(*)(int)))(int);

同樣地,使用typedef可以簡化上面的函式宣告:

1

2

typedef void (*handler)(int);

handler signal(int , handler);

那麼,現在的你對函式指標理解了嗎?如果你看完了此篇文章,相信你一定會有意想不到的收穫哦!

參考書籍:c陷阱與缺陷

詳解C C 函式指標宣告

要理解乙個c程式,僅僅理解組成該程式的符號是不夠的。程式設計師還必須理解這些符號是如何組合成宣告 表示式 語句和程式的。我們先來看看下面的乙個語句 1 void 0 這是當計算機啟動時,硬體將呼叫首位址為0位置的子例程。像這樣的表示式恐怕會令每個c c 程式設計師的內心都 不寒而慄 吧。然而,完全不...

詳解C C 函式指標宣告

要理解乙個c程式,僅僅理解組成該程式的符號是不夠的。程式設計師還必須理解這些符號是如何組合成宣告 表示式 語句和程式的。我們先來看看下面的乙個語句 1 void 0 這是當計算機啟動時,硬體將呼叫首位址為0位置的子例程。像這樣的表示式恐怕會令每個c c 程式設計師的內心都 不寒而慄 吧。然而,完全不...

c c 函式指標詳解

今天我們要介紹的是函式指標。內容由以下3部分組成 函式指標的概念,為了面試時能夠說出來。函式指標的用法 當然是為工作用到嘍 函式指標的例項,用一些練習來加深印象。經典面試題 函式指標和指標函式的區別 好 開始!函式指標的概念 1.1 函式指標 是指向函式的指標變數 在程式執行中,函式是程式演算法指令...