【前言】
由於最近對函式指標的理解比較模糊,所有又重新學習了一把關於函式指標的知識,參考了很多書籍和網上的文章。現在本人進行一下分享和總結。本文的其實只是整理和總結別人現有的文章,作為備用參考文件。
【正文】
要理解乙個c程式,僅僅理解組成該程式的符號是不夠的。程式設計師還必須理解這些符號是如何組合成宣告、表示式、語句和程式的。
我們先來看看下面的乙個語句:
[cpp]view plain
copy
( *(
void
(*)())0)();
這是當計算機啟動時,硬體將呼叫首位址為0位置的子例程。像這樣的表示式恐怕會令每個c/c++程式設計師的內心都「不寒而慄」吧。然而,完全不用害怕,任何c變數的宣告都是由兩部分組成:型別以及一組類似表示式的宣告符。最簡單的宣告變數,如:
[cpp]view plain
copy
float
f , g ;
這個宣告的含義是:當對其求值時,表示式f和g的型別為浮點型。同樣的邏輯也適用於函式和指標型別的宣告,例如:
[cpp]view plain
copy
float
ff();
這個宣告的含義是:表示式ff()求值結果是乙個浮點數,也就是說,ff是乙個返回值為浮點型別的函式,類似地:
[cpp]view plain
copy
float
*pf;
這個宣告的含義是*pf是乙個浮點數,也就是說,pf是乙個指向浮點數的指標。
以上這些形式在宣告中還可以組合起來,就像在表示式中進行組合一樣,因此:
[cpp]view plain
copy
float
*g() , (*h)();
表示*g()與(*h)()是浮點表示式。因為()結合優先順序高於*,*g()也就是*(g()):g是乙個函式,該函式的返回值型別為指向浮點數的指標。同理,可以得出h是乙個函式指標,h所指向函式的返回值為浮點型別。
一旦我們知道了如何宣告乙個給定型別的變數,那麼該型別的型別轉換符就很容易得到了:只需要把宣告中的變數名和宣告末尾的分號去掉,再將剩餘的部分用乙個括號整個「封裝」起來即可。例如:
[cpp]view plain
copy
float
(*h)();
表示h是乙個指向返回值為浮點型別的函式的指標,因此,
[cpp]view plain
copy
(float
(*)())
表示乙個「指向返回值為浮點型別的函式的指標」的型別轉換符。
那麼,我們現在來看看前面我們提出的表示式:
[cpp]view plain
copy
( *(
void
(*)())0)();
第一步,假定變數fp是乙個函式指標,那麼如何呼叫fp所指向的函式呢?呼叫方法如下:
[cpp]view plain
copy
(*fp)();
因為fp是乙個函式指標,那麼*fp就是該指標所指向的函式,所以(*fp)()就是呼叫該函式的方式。表示式(*fp)()中,*fp兩側的括號非常重要,因為函式運算子()的優先順序高於單目運算子*。如果*fp兩側沒有括號,那麼*fp()實際上與*(fp())的含義完全一致。現在剩下的問題就只是找到乙個恰到的表示式來替換fp。
我們將在分析的第二步來解決這個問題。如果c編譯器能夠理解我們大腦中對於型別的認識,那麼我們可以這樣寫:
[cpp]view plain
copy
(*0)()
上式並不能生效,因為運算子*必須要乙個指標來做運算元。而且這個指標還應該是乙個函式指標,這樣經運算子*作用後的結果才能作為函式被呼叫。因此,在上式中必須對0作型別轉換,轉換後的型別可以大致描述為:「指向返回值為void型別的函式的指標」。
如果fp是乙個指向返回值為void型別的函式的指標,那麼(*fp)()的值為void,fp的宣告如下:
[cpp]view plain
copy
viod (*fp)();
因此,將常數0轉型為「指向返回值為void的函式的指標」型別,可以這樣寫:
[cpp]view plain
copy
(void
(*)())0
因此,我們可以用(void(*)())0來替換fp,從而得到:
[cpp]view plain
copy
( *(
void
(*)())0)();
當然,我們用typedef來解決這個問題能夠表述更加清晰:
[cpp]view plain
copy
typedef
void
(*fp)();
(*(fp)0)();
這個問題就可以解決了。
我們再來考慮signal庫函式,一般情況下,程式設計師並不主動宣告signal函式,而是直接使用標頭檔案signal.h中的宣告。那麼,在標頭檔案signal.h中,signal函式是如何宣告的呢?
首先,讓我們從使用者定義的訊號處理函式開始考慮,這無疑是最容易解決的。該函式可以定義如下:
[cpp]view plain
copy
void
sigfunc(
intn)
函式sigfunc的引數是乙個代表特定訊號的整數值,此處我們暫時忽略它。上面假設的函式體定義了sigfunc函式,因而sigfunc函式的宣告可以如下:
[cpp]view plain
copy
void
sigfunc(
int);
現在假定我們希望宣告乙個指向sigfunc函式的指標變數,不妨命名為sfp。因而sfp指向sigfunc函式,*sfp就代表sigfunc函式,因此*sfp可以被呼叫。因此我們可以如下這樣宣告sfp:
[cpp]view plain
copy
void
(*sfp)(
int);
因為signal函式的返回值型別與sfp的返回值型別一樣,上式也就宣告了signal函式,我們不妨可以如下宣告signal函式:
[cpp]view plain
copy
void
(*signal(something))(
int);
此處的something代表了signal函式的引數型別,我們還需要進一步了解如何宣告它們。上面宣告可以這樣理解:傳遞適當的引數以呼叫signal函式,對signal函式返回值(為函式指標型別)解除引用,然後傳遞乙個整型引數呼叫解除引用後所得函式,最後返回值為void型別。因此,signal函式的返回值是乙個指向返回值為void型別的函式指標。
那麼,signal函式的引數又是如何呢?signal函式接受兩個引數:乙個整型的訊號編號,以及乙個指向使用者定義的訊號處理函式的指標。我們此前一定定義了指向使用者定義的訊號處理函式的指標sfp:
[cpp]view plain
copy
void
(*sfp)(
int);
sfp的型別可以通過將上面的宣告中的sfp去掉而得到,即 void(*)(int)。此外,signal函式的返回值是乙個指向呼叫前的使用者定義訊號處理函式的指標,這個指標的型別與sfp指標型別一致。因此我們可以如下宣告signal函式:
[cpp]view plain
copy
void
(*signal(
int,
void
(*)(
int)))(
int);
同樣地,使用typedef可以簡化上面的函式宣告:
[cpp]view plain
copy
typedef
void
(*handler)(
int);
handler signal(int
, handler);
c 筆記,指標的定義
牛客網上看見別人整理的,恍然大悟 把 讀作 pointer to 從右至左唸 b是乙個常量 const int b b is aint const int const b b is aconst int p是乙個普通指標,指向乙個常量 const int p p is apointertoint c...
C 指標的定義和指標的運算
一談起指標,會不會有人跟我一樣感到有些害怕?最開始在學習c語言時,剛接觸指標,讓我感覺很頭大,感覺沒能很深入的理解。在重新學習理解了之後,寫了這篇部落格。指標 pointer 是程式語言中的乙個物件,利用位址,它的值直接指向存在電腦儲存器中另乙個地方的值。由於通過位址能找到所需的變數單元,可以說,位...
c 定義指標 delete
c 無論何種資料,是否要delete關鍵看其空間是否使用new分配的。1,函式衝定義的區域性變數指標,單純是乙個區域性變數是不用delete 2,c 如果類中有乙個指標資料成員,而沒有用new,析構函式也是不用delete的 在類或函式中,int char 這些只要不是new的,也同樣不用釋放,系統...