0x00 最初的起點
首先是一道爛大街的題目:以下四個變量有什么區別?
答案很簡單:
1. v1是一個指針常量,即指針所指向的值不可改變,但是指針所指向的地址可以改變。
2. v2和v1相同。
3. v3是一個常量指針,即指針所指向的內存地址不可改變。
4. v4是一個指向常量的常量指針,即無論是指向的內存地址,還是內存地址中的值都是不可改變的。
可以看到,這個基本上就是依靠記憶的東西,并沒有什么理解上的難點,只是記住之后沒過一段時間可能就又混淆了。
記憶方案一
1.如果const位于的左側,則const就是用來修飾指針所指向的變量,即指針指向為常量;
2.如果const位于的右側,則const就是修飾指針本身,即指針本身是常量。
恩... 似乎很有規律的樣子,但是我記性很差,這種規則對我來說還是太繞,過一段時間就不確定會不會記反了。
記憶方案二
Bjarne在他的The C++ Programming Language里面給出過一個助記的方法:
把一個聲明從右向左讀。
char * const cp; ( * 讀成 pointer to )
cp is a const pointer to char
const char * p;
p is a pointer to const char;
恩。這樣確實大大的降低了記混的情況了,但是雖然這種方法已經很簡單了,但是對于英語不是母語的人來說,還是需要繞一層的,那么有什么更加簡單的記憶方法嗎?
記憶方案三
const修飾前面的關鍵字,只有當const開頭時才修飾后面的關鍵字
即:當const前面是char int
之類的類型關鍵字時,表示修飾的是指針所指向的內容;而當前面的是指針運算符(*
)的時候,那么表示修飾的是該指針;當const位于表達式首位時,請參考第一條。
0X01實現機制
其實const關鍵字并沒有多么復雜的實現方法,只是在編譯器的層面做了modify的限制,而const變量和非const變量在runtime期間其實并無卵分別,最好的證據就是編譯過程中的匯編代碼了,證據如下:
首先我們有兩段簡單到媽都不認得的C++代碼:
代碼一
#include <iostream>
using namespace std;
int main()
{
int a = 100;
return 0;
}
代碼二
#include <iostream>
using namespace std;
int main()
{
const int a = 100;
return 0;
}
這兩段代碼唯一的區別就是,一個int變量有const修飾,而另一個int變量沒有const修飾。OK,接下來我們編譯成匯編,使用下面的命令:
g++ -S demo.cpp
編譯之后得到的匯編代碼對比圖如下所示:
我們可看到,兩者并無什么不同,也就是說,匯編并不會對const的變量做什么特殊的處理,const只是在編譯的層面給程序做了限制。
但是,這里又有一個問題,當我們把代碼修改成下面那樣的時候:
代碼一
#include <iostream>
using namespace std;
int main()
{
int a = 100;
cout << a << endl;
return 0;
}
代碼二
#include <iostream>
using namespace std;
int main()
{
const int a = 100;
cout << a << endl;
return 0;
}
這里的代碼與上面的代碼的區別無非是,訪問了a的值,按照上面的理論,編譯后的代碼應該沒有不同,但是世事難料,對比圖如下:
我們可以看到竟然還是存在差別的,但是根據查閱資料和個人猜測,我認為可能的原因是:const的變量存儲的時候確實沒有特殊處理,但是在取值時,并不會訪問const變量的內存地址,而是通過一個全局符號表(symbol table)替換了該值
,所以才會出現這樣的匯編代碼的差別。
0X02 最后說的話
說到底還是不懂匯編,有空也可以研究一下,如果有匯編大神路過,也請不吝賜教!