指針是C++中極其重要的概念,若是說某汪在華語樂壇佔據了半壁江山,那麼指針也是支撐C++衆多高級特性的一雙有力的大手。本文簡要說明C++中指針的定義及指針類型的判斷,其中指針類型的判斷每每是初學者最大的障礙,但這也是最須要重點掌握的基礎技能程序員
從各類教科書上大同小異的定義來看,指針就是保存其餘變量的地址的變量。要想弄懂這個定義,咱們須要先搞清楚兩個概念:變量、變量的地址。編程
變量是用於存儲某一數值的「容器」(「容器」這一詞在C++和其餘領域中是專業術語,這裏只是用於比喻,不要把這個詞當成對變量的術語,牢記!),咱們在計算機中編程時,整體來看都是「輸入——計算——輸出」這一套路,因此每每會有大量的數據,若是程序中所有的數據都是使用字面常量的方式來記錄,那麼先不說可實現性,光光是程序的編寫和維護就是一件幾乎不可能的事了,因此從BCPL和B語言以後(準確地說是C語言後),正式有了變量的概念。程序員只須要定義好某種類型的變量,就能夠存儲對應類型的數據,而不需再再像在無類型語言裏那樣關心數據在內存中是如何存儲的了。數組
這裏所說的內存模型是高級語言程序員腦中的內存模型,實際上並不存在;內存的物理模型不須要高級語言的初學者考慮,故不在此討論
程序運行在內存中,數據和指令都加載進內存中(然而實際上並非這麼理想,在以後的文章中我會再介紹操做系統對內存的管理),因此爲了讓訪問內存的設備能準確地定位到某一處,內存的每一處都分配了一個惟一的數值,至關於身份證號,稱做地址。內存地址是二進制的數值。32位機的尋址能力就是能在由32位標識的內存地址中進行定位,因此內存大小被限制在4GB。
地址這一詞與人類社會的門牌號很像,人住在屋子裏,而屋子有一個編號,人看做數據,屋子就是存儲「人」這一類型的變量,而門牌號就是變量的地址。函數
看了以上這麼多的文字,那麼指針的類比概念也很好理解了,不過到此咱們須要回答一個問題:爲何須要指針?經過指針來操做變量仍是一個二級的操做,這不會顯得很麻煩嗎?操作系統
是的,經過指針咱們確實比直接操做變量多作了工做,但偏偏就是指針,也給了程序員直接操做內存的機會,由於直接操做內存比操做變量在時間上要快上不少,這也是爲何C++程序運行的效率高的緣由之一。指針
指針(pointer)的全名實際上是指針變量,也就是說指針也是變量。既然是變量,那麼很天然的也就有類型這一問題。C++是強類型的靜態語言,某一種類型的變量就只能存儲其對應類型的數據,指針也不例外。一般,當某一類型指針保存着其對應類型的地址時,咱們說「這個指針指向了那個變量」。code
正文從這開始。
把指針考慮成變量,因此咱們從最基本的變量(以整型爲例)的定義開始分析。內存
int p;
數學
p是一個整型變量。入門
int *p;
p是一個指向整型變量的指針。
int p[SIZE];
p是一個數組,數組中有SIZE個元素,每一個元素都是整型。
int *p[SIZE];
從這裏開始變得有趣了。
對於這種「複雜」類型的定義,有一種方法是從中間開始讀,根據運算符優先級向兩邊擴展。
p是一個數組(先與定義數組的下標運算符結合,由於下標運算符[]
的優先級高於解引用運算符*
),數組中有SIZE個元素,而後與*
結合,數組中的元素都是指針,最後看int
,即指針指向int
。因此,把以上的分析過程連起來,就是,p是一個由SIZE個指向整型的指針構成的數組。
int (*p)[SIZE];
這裏咱們會開始簡單看到()
運算符的其中一個做用。
首先,從p開始尋找優先級比*
高的運算符——是的,()
在這裏的意義和數學中的小括號是同樣的,都是用於強行改變運算優先級的運算符。因此,由於有()
,p先與*
結合,p是一個指針,而後再與[]
結合,p這個指針指向一個有SIZE個元素的數組,最後,看int
,即,p是一個指向由SIZE個整型元素構成的數組的指針。
int p(int);
這裏就是()
的另外一個做用。
這裏的()
是函數調用運算符,()
內寫清了參數列表,因此,p是一個函數,函數有一個整型變量的參數,函數返回值爲整型。
int p(int, int *, int);
分析過程同上一點,直接給出結果。
p是一個函數,有三個參數,依次爲整型、指向整型的指針和整型,返回值爲整型。
int **p;
C++中沒有**
這種的運算符,因此這種寫法是等同於int *(*p);
的。
其表示,p是一個指針,指向了一個指針變量,且被p指向的那個指針指向整型。
這種指針稱做二級指針,相似的,還有多級指針,但在實際編寫程序時,二級指針的使用已經比較少了,多級指針更是幾乎(不表明沒有用處)不會使用,因此初學者沒必要考慮多級指針。
int (*p)(int *, int)
;
p是一個指針,指向了一個函數,這個函數的參數列表爲(int *, int)
,返回值爲int
。
這裏的p,咱們就稱之爲函數指針,由於它指向了函數。函數指針的概念須要實際練習幾回才能初步掌握。
void (*p[SIZE])(int *, int);
這種複雜程度的類型定義是程序中常常見到的,因此一點一點地來分析。
p先與[]
結合,說明p是一個數組;再與左側的*
結合,說明數組裏的元素都是指針;以後與右側的函數調用運算符()
結合,那些指針指向的函數的參數依次爲(int *, int)
,返回值類型爲void
。因此,再整理一下剛剛的分析:
p是一個由SIZE個元素構成的數組,每一個元素都是函數指針,其指向的函數的參數依次爲指向整型的指針、整型,返回值類型爲void
。
這種類型要求重點掌握。
int *(*p(int))[SIZE];
這種類型的能夠先跳過,在確認了掌握了上面講述的全部定義後再來嘗試理解。
從p開始分析,先與()
結合,說明p是一個函數;而後進入到參數列表中,發現這個函數的參數只一個int
;再看p左側的*
,說明這個函數的返回值是一個指針;以後到外層(這裏外層的()
是爲了強制改變優先級),先與[]
結合,說明函數返回的指針指向的是一個數組;再與左面的*
結合,說明指向的數組中的元素都是指針;最後與int
結合,說明數組中的元素指向整型。
因此,p是一個參數爲一個int
、且返回一個指向由SIZE個int *
構成的數組的的指針的函數。
這裏思考,若是去掉最外層的`()`,即改成以下: `int **p(int)[SIZE];` 會是什麼結果?爲何?歡迎在以後的討論區發表意見。
經常使用的指針類型(或者直接說是類型)定義大多都是如上類型的變種,固然會有更復雜的,不過再複雜的均可以用上述的方法來進行分析。