【C++關鍵字】const 用法 深入 分析。

【不想探究深入機制的可以直接看第7部分】

1.  最初的用途: 代替預編譯指令 #define,可以縮小程序size,從而節省載入到內存運行時的內存開銷。

舉例:(網上的例子多半都是CV黨,不懂的人看了會更加不懂,更怕C++)百度百科給的例子就是錯的,或者說不恰當的,解釋的方法不對。

比如:

/--------------------------------------------------------define--------------------------------------------------------------/

#define tbwood 12

int a=tbwood;

int b=tbwood;

編譯後爲(彙編語句:mov語句是把 後面一個數(或者變量的值) 移動到 前面一個變量中):

mov a 12;  //立即數尋址

mov b 12;

/--------------------------------------------------------const--------------------------------------------------------------/

再看const

const int tbwood=12;

int a=tbwood;

int b=tbwood;

彙編後代碼爲:

mov a [tbwood] //直接尋址,把tbwood對應的內存塊數據移動到a變量中

mov b [tbwood]


那麼從目前來看,我們發現const方式比define 甚至還要差一點,因爲兩種代碼編譯後的大小是一模一樣的,都是 2*sizeof(mov instruction)+4*sizeof(int),但是const方式是直接尋址,比立即尋址稍微慢了點。 這就是“空間換時間”的好例子。

但是如果我們想,假如現在我們定義的常量是 一個長整數,或者是很長的字符串 長度 >sizeof(int)   字節,設爲 M (M>sizeof(int) )

那麼這時候

define 方式下編譯的代碼 長度爲: 2*sizeof(mov instruction)+2*sizeof(int)+ 2*M 

const   方式下編譯的代碼長度爲: 2*sizeof(mov instruction)+4*sizeof(int)

這時候我們發現,const比define節省了  2*(M-sizeof(int)) 的字節空間。

上面我們只是使用了兩次,如果程序中常量出現了K次,那麼節省的空間爲:

K*(M-sizeof(int))    (M爲常量的字節數長度)

如果長度大於4字節(整數的長度 -32位程序)的常量在一個程序中很常見,那麼可以節省很大的空間。

第一點與 define的比較總結下就是: 節省程序空間。


2. 以上說的是const的最初設計的目的。其實上述特點普通變量就可以擁有, 歸結一下就是: 變量是運行時分配,立即數是編譯時分配的。const 方法只不過是用了個變量名而已。

比如:

int tbwood =12;

int a=tbwood;

int b=tbwood;

那爲什麼不直接用int 而用const int 呢。或者說,後者比前者的優勢在哪。

其實沒啥神奇的,就是const int 定義的變量是不可修改的,而int直接定義會被修改。減小程序員出錯的概率,或者軟件運行時被入侵,導致安全問題。

如果const 定義的變量在運行時被入侵或者篡改,那麼會導致錯誤,或者無法修改,因爲常量放在常量數據段中,有寫保護。

所以第二點與普通變量方式的比較總結下就是: 更加安全。


3. const 變量 作爲函數參數的好處: 防止在函數內部不小心修改到該參數,這是編程時的好處。


4. const int * p 和 int * const p 的區別。

(1)首先 const int *p 和  int const * p是一樣的。都是表示  *p 是常量,p是指向常量的指針變量。 這樣的情況,嘗試 用 *p=2 就是錯誤的。

(2)int *const p 和 const (int*) p 是一樣的。但是後者在C/C++編譯器裏是不支持的,所以一般這麼寫: typedef int* ptbint; const ptbint p。 這種寫法和int *const p是一樣的,          語義爲定義一個靜態的指針變量p。這種情況下,嘗試 p++ 或者p=2 是錯誤的。


5. const 引用

 (1)嚴格的const引用 是指: const int a=10;  int &b=a;  b是const引用。【Cited from C++ Primer】

 (2)這裏我們說的const引用是:

  那麼我們這裏的const 引用具有哪些特點呢?

  int a=10; 

  const int & b=a;

 很簡單: a 和 b 指向的內存單元(現在存放的是10)只能由a來修改,b不能修改。不能使用 b=11;

 那麼我們會有一個疑問,程序運行的時候,計算機怎麼知道 這個地址 是const 還是普通呢,同一個地址,而且是普通變量a的,總不能一個變量同時放在常量段和變量段吧。(答案是放在變量段,因爲這個引用指向的是一個普通變量,如果是(1)的情況,那麼a b都放在常量段,雖然用b去修改這個值編譯不會出錯,但是運行時仍然不會修改)

其實是想多了,這個錯誤是在編譯的時候就會提示了,所以壓根不會編譯成功。所以這種引用方式的作用還是:“防止程序員編碼出錯”。

那麼這種const應該用在哪裏呢?

一般用在函數的參數中,比如:

void func(const int &a)

{

  .....

}

這樣的好處是,(a)防止程序員在寫實現implement的時候不小心“顯式的(就是很明顯的)”修改到a。如果修改了,那麼編譯不會通過。

還有哪些好處呢?(b)const int & a 具有 int&a的特性,傳名方式,我們知道,好處就是快、省,不需要開闢臨時參數。

       (c)也有些說法說可以完全保護a不被修改,下個部分總結的時候我會否認這個好處。



6. 總結:

const 在編譯時的作用有: 

(1)節省程序大小。

(2)防止用戶顯式的修改變量(換句話說,防止程序員犯簡單錯誤)。

const 在運行時的作用有:

(3)答:對於直接定義的const 變量,運行時也會受保護。比如const int a=10;那麼 不管你通過什麼方式修改,a都不會變。

這裏需要注意 使用 const int  & a 時,

如果直接用立即數: const int & a=40; 那麼 a不會放到常量區,而是普通變量區。

如果使用變量賦值: const int & a=b; 那麼如果b放在常量區,那麼a也是常量區地址,如果b放在普通區,那麼a也是普通數據區地址。

如果把立即數40也看成是“非常量區地址”(在代碼區),那麼可以得到,如果右值是常量區地址,那麼該變量也是常量區地址,否則是普通數據區地址。

我的中文名是 王小二,英文名是 Dr Wang,那麼如果王小二是博士, Dr Wang肯定也是博士。

其實這個和   int & a = 右值     一個道理。

之所以加上const 是爲了防止下面用戶不小心修改a。但是這種“防止修改”是十分弱,還是要看右值。

如果右值 是常量區,那麼代碼中不管怎麼修改a都不會變。

比如:

const int b=40;

const int & a=b;

*((int*)&a)=10;

這時候a的值還是40。因爲a在常量區。


如果右值不在常量區,在普通數據區或者代碼區立即數,那麼a還是可以改變的。

比如:

const int & a=40;

*((int*)&a)=10;

這時候a的值就是10了。修改了,很輕鬆吧。

這個例子裏的右值其實就是 func(const int & a ) 裏的實際參數

一個道理,所以最終能不能“徹底的”保護這個變量,還是要看傳遞進來的參數地址是在常量區 還是在非常量區。

另一個知識點:

(4)const int & a  還可以用來防止程序員犯語法錯誤【C++ primer 中文版上寫的含糊其辭,但是我覺得原版就是下面這個意思】

比如:

double a=3.14;

int const & b=a; //或者去掉const 也一樣

這個時候 a 和b 會存放在不同的地址內存中。

編譯的彙編僞代碼爲:

mov temp, a;

get interger part from temp;

set b to point to temp;


C++程序員如果直接修改b,那麼會被提示錯誤。

而不加const 的時候,編譯會通過,給程序員帶來十分困惑的煩惱。




7. 關於const,作爲一個合格的C++程序員,需要記住什麼。

說實在的,要把這麼多細節記住,除非是不想學其他東西了,人的大腦記憶量是有限的。

關於const 我們只要記住:

(1)那些被定義成const的變量 都會被放在 常量區(const type & a 不算),從而無法修改,就算編譯通過,也無法修改。

(2) const type a 的好處是節省程序空間,防止用戶修改。

(3)const type& a 的好處是:

            (a)  防止用戶顯式的修改a,用在函數形參時還可以提高速度,避免創建內存。

            (b)  如果把 不同類型的右值rv 賦值給 a, 那麼a會另外分配地址,const的作用只是爲了提醒程序員  不要嘗試用a來改變rv。

(4)還有就是 type * const a 和 const type * a的區別:前者定義一個靜態指針變量,其指向的地址不可變;後者是定義了一個指針,指向一個不可變的內存數據。內存分配如圖:


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章