理清C++常量指針和指針常量這團亂麻

寫在前面:javascript

     與其說C++中的常量指針和指針常量是一塊頗有嚼頭的語法糖,不如說它是一塊至關難啃的骨頭。其實原本沒什麼,這無非是const int *p與int* const p的區別, 但一涉及到起名字,特別是給他們戴上「常量指針」和「指針常量」的中文帽子,因爲做者和譯者(針對外文書)的不一樣,就出現了「張冠李戴」和「李冠張戴」的亂像,不知道誰是誰了,弄得人一頭霧水,尤爲是對於初學者。本文的目的就是針對這一細節,爲你們將二者理清楚,同時說明在使用上的區別。java

注意:1.const int *p也可寫成int const *p,即C++中const int和int const無區別,這使得原本就很亂的局面更加麻煩,本文中我只使用const int,之後再也不說明。ios

        2.若是您討厭囉嗦,只想學「乾貨」,您能夠直接跳到「安能辨我是雄雌——判斷方法」一節(在下技術有限,就不設置頁面內跳轉了)。編程

在理清楚以前,讓 咱們先簡單看看當前「亂象叢生」的現狀吧。安全

1、const趕上指針——一團亂麻網絡

一樣的問題,相反的解釋this

1.標新立異的少數派spa

      C++ Primer第五版的提法可謂與其它C++書籍背道而馳,它的提設計

                                                                                            常量指針——int* const p指針

指向常量的指針——const int *p

      在英文版中,int* const p被稱爲const pointer,因而中文版將其譯爲常量指針;而const int *p被稱爲pointer to const,但對應的中文版並未將其譯爲指針常量,而是譯爲指向常量的指針。

      無獨有偶,咱們國內的書籍《零起點學通C++》(範磊 著 清華大學出版社 2010年版)的提法和C++ Primer徹底一致,將int* const p稱爲常量指針,而將const int *p稱爲指向常量的指針。

2.一邊倒的大衆說辭

      更廣爲流傳的提法是

常量指針——const int *p

指針常量——int* const p

      明確寫有「常量指針」和「指針常量」的書籍在下就見過一本——《C++面向對象程序設計》(郭有強 著 清華大學出版社 2009年版)(從出版年上咱們能夠大膽推測,相對較老的書會這樣提)。更多的書籍的作法是——將int* const p稱爲指針常量,將const int *p稱爲指向常量的指針。(可見問題的關鍵在於int* const p,有的書中將其稱爲常量指針,如1,有的書中將其稱爲指針常量,如2)

      這裏的「一邊倒」更主要的是針對網絡而言。若是您百度「常量指針和指針常量」,將會看到幾乎全部的材料中都採用了2中的提法。若是一個初學者在解決「常量指針和指針常量」的困惑時首先選擇了網絡途徑,那麼2的提法應該在他的腦海裏根深蒂固了吧。

      若是您已經接受了2中的提法,建議您將「常量指針」擴展爲「指向常量的指針」,將「指針常量」擴展爲「該指針是常量」來幫助您記憶和區別二者的本質。

做者們的拯救

      大概一些IT教育者和從業者們也注意到了這一問題,近年來的一些書籍做者們開始用本身的方式來糾正,讓咱們來看看他們是怎麼作的。

1.這樣的改變好嗎?

     《C++程序設計基礎》(管建和 著 清華大學出版社 2013年版)中將const int *p稱爲「常量化指針目標表達式」,而將int* const p稱爲「常量化指針變量」,試問,這麼長的名字,能不能記住?記住了,能不能搞清楚?

2.兩個弄不清,乾脆叫一個

      比較著名的外文書籍《C++高級編程》(這裏就中文第二版講)中在「const指針」的標題下同時介紹了const int *p和int* const p,而沒有給他們具體起名字。

      在國內,《新標準C++程序設計教程》(郭煒 著 清華大學出版社 2012年版)將const int *p稱爲常量指針,而在介紹int* const p時寫道「還有一種常量指針」。

      但這樣作仍然不能稱之爲行之有效,由於這樣並無經過名字將二者從本質上區分,同時「不一樣的事物,同一個名字」也不便於交流。

3.一種值得推薦的命名法

《C++程序設計教程》(方超昆 著 北京郵電大學出版社 2009年版)和《C++從入門到精通》(人民郵電出版社 2015年版)是這樣提的

指向常量的指針變量——const int* p

指向變量的指針常量——int* const p

                                  指向常量的指針常量——const int* const p   //後面會詳細說到

      這樣,誰常誰變就一目瞭然了,並且名字反應了本質。從第一本書的出版年能夠看出,其實這樣比較清晰的命名方式好幾年前就有了,但惋惜的是,直到如今,在衆多的C++書籍中,這般清晰的命名方式也並很少見。

在下的用意

      必定會有人說:「你說了這麼多,並無給咱們帶來什麼實質性的知識。」在下不反對,其實在下在這一部分的開頭已經清醒過你們,能夠略過。在下寫這部分的用意有四:

1.向讀者介紹一下現狀和常見的提法。

2.告訴讀者該問題上存在的不一致現象,當讀者閱讀不一樣的書籍時遇到不一致,不要過於困惑。

3.勸讀者不要記憶網上的「常量指針」和「指針常量」,由於這樣的命名並很差,許多書籍上也並無同時提到這二者。

4.表達一下本身但願C++的相關命名實現術語化、標準化、大一統的願望,儘管在下知道這一點很難實現。

在下怎麼看

1.不要使用「常量指針」和「指針常量」,特別是後者,由於它連「表述對象是指針」這層意思都沒有清晰地表達出來。

2.建議採用「一種值得推薦的命名法」下的名稱來記憶。

3.或者採用在下的建議。如今說明在下的提法

指向常量的指針——const int *p

   自己是常量的指針——int* const p

                       所指和自己都是常量的指針——const int* const p

       這裏採用「定語+主體」的結構,主體「指針」很好地表現了所描述對象是指針這一本質,而定語部分則描述了特性,同時也將「本身」和「他人」區別開,總體通俗易懂。(在下一直認爲,有時一個高大上的名稱就是故做高深,而通俗易懂自己就是一種真正的高大上。面向初學者,把話說明白,一直是在下文章的宗旨與追求。)惟一的不足多是名稱有點長,但在下相信,只要足夠通俗易懂,長不是障礙。

       從如今開始,咱們在後面的講述中將一直採用這種提法。

2、安能辨我是雄雌——判斷方法

      相信有些初學者會有這樣的困惑:單提名字知道怎麼回事,但一看到代碼就不知道叫什麼名字了,也就沒法想起代碼的語法特性。這裏,在下給你們介紹一種簡單易用的判斷方法,一看代碼就能分析出語法特性。

      相信有些讀者看到過所謂的「從右往左讀」的辦法,在下接下來要說的方法和「從右往左讀」本質上同樣,只不過不管是認知仍是操做上都更加簡單(起碼在下認爲是這樣,這裏就不謙虛了)。方法表述以下:

先找到*,而後看*的兩邊,右邊是對指針p自己的限定,左邊是對p所指向的東西的限定。

 注:本文中咱們將p的所指籠統地稱爲「東西」。

例如

1.const int *p

      *的右邊沒有限定成分,代表p就是咱們熟悉的普通指針,p的內容(即值,也就是它指向的東西的地址)是能夠改變的;*的左邊是const int,代表p指向的東西是一個const的int,咱們不能經過p來修改這個int,由於它是const的。(關於「不能經過p來修改」,咱們後邊還會詳細解釋)

2.int* const p

      *的右邊是const,代表p自己是const的,咱們不能對p的內容進行修改(例如++p;是不能夠的),*的左邊是int,即p指向的東西是普通的int,咱們能夠經過p來修改它(例如*p=100;是能夠的)。

3.const int* const p

      *的右邊是const,代表指針p自己是const的,*的左邊是const int,代表p指向的int也是const的。即這種狀況下,p自己不能修改,同時也不能經過p修改它所指向的那個int。

3、天生我材必有用——語法特性

      其實在講判斷方法的時候,已經涉及到了各自的語法特性。這裏再針對一些細節深刻說一下。

1.const int *p

      就是所謂的「指向常量的指針」。這裏注意,所謂「指向常量」只是這個指針的「一廂情願」,只是一種效果上的等價。事實上,const int *p=&a;a既能夠是常量(const int a=10;)又能夠是變量(int a=10;),但p一廂情願地認爲它所指的就是一個常量,因此它不容許經過本身來修改所指,這就形成一種效果上的等價——從p的角度看,它所指的「的確」是常量。因此,對「指向常量的指針」的最佳理解應爲:咱們不能經過該指針修改它所指向的東西(常量或者變量)。

      注意,const int *p=&a;只是說不能經過p來修改a,若是a自己不是const的,經過其它方式修改a天然是能夠的(例如直接++a)。

      另一點,因爲p自己只是一個普通的指針,因此容許在聲明時不初始化。但須要注意的是,咱們只是說能夠,但並不提倡這樣作。在任什麼時候候都不該該讓指針無所指,若是在聲明一個指針時還不知道讓它指向誰,就先將其初始化爲nullptr或NULL(nullptr是C++11新特性,用它比用NULL更安全些,這裏不詳細介紹)。

2.int* const p

      就是所謂的「自己是常量的指針」。關於「p自己不能修改但能夠經過p修改其所指」這一點,咱們在講判斷方法時已經說過,這裏主要再說一下p的初始化。

      因爲p自己是const的,因此在編譯的時候必須知道p的值(即p所指向的東西的地址),因此在聲明p的同時必須初始化p。但要注意,對於 int* const p=&a,咱們只要求a的地址是肯定的,但a的值能夠不肯定。好比下面的代碼是可行的

複製代碼
#include<iostream>

using namespace std; int GetData(int num) { return num; } int main() { int a; cin >> a; int b = GetData(a); int* const p = &b; cout << *p << endl; return 0; }
複製代碼

因爲聲明瞭int b,因此b的地址在編譯時是肯定的,但很顯然,b的值只要在程序運行時才能肯定。

      另外注意,用nullptr或NULL初始化int* const p沒有問題,由於nullptr和NULL都表明有效地址。

3.const int* const p

     就是所謂的「所指和自己都是常量的指針」。它的語法特性就是前二者的結合,這裏再也不贅述。

4、番外篇——說說引用和const引用

      文章至此本該結束了,但就C++而言,談到了指針,彷佛天然應該再談談引用。因而,在下接着再來簡單說說引用和const引用。所謂「簡單說說」,是指在下只是作一個簡明扼要的介紹,對一些原理和細節不作深刻探討。爲何要在這裏說?由於咱們的講述要會用到上面的概念。另外還要注意,咱們這裏只討論左值引用,即你們所熟悉的通常引用,對於C++11新特性右值引用,不在討論之列。

相信你們都知道引用的這些特性

1.若是一個引用和一個東西(這裏再次使用「東西」這個詞)綁定,那麼它就永遠只能是這個東西的「別名」,不能再說「其餘人」的「別名」,即引用自己不能修改。可是,咱們能夠經過引用來修改它所引用的東西的值。

2.引用聲明時必須同時初始化 ,且必須用左值初始化。(左值:就是能夠用&求地址的量,換言之,就是有肯定地址的量,而不是所謂的臨時量)

       對這些特性你們是否是似曾相識呢?沒錯,這些特性和「自己是常量的指針」(int* const p)的特性同樣。事實上,咱們徹底能夠借用「自己是常量的指針」來理解甚至定義引用:

引用是一個指向不可變的、被編譯器自動解引用的指針,即,引用是一個被編譯器自動解引用的「自己是常量的指針」。

看下面的代碼

int a = 10; int &ra = a; ra = 11;

      在上面的代碼中,編譯器將int &ra=a轉化爲int* const ra=&a,而將ra=11轉化爲*ra=11,將ra自動轉化爲*ra的過程,就是上面定義中所說的「自動解引用」。

      那麼,什麼是const引用(即咱們說的常量引用,但我但願你們稱其爲const引用而不是常量引用)呢?很顯然,const int &ra=a就至關於const int* const ra=&a了。相信經過前面的講解,這裏不用在下多說了。

後記:

      在下能力有限,儘管盡最大努力作到嚴謹,但錯誤疏漏之處仍在所不免,懇請你們批評指正。您的幫助,就是在下前進的不竭動力。

相關文章
相關標籤/搜索