最近在看程序員面試寶典,看到const這塊感覺有很大疑惑,查了很多資料,可以總結如下:
1.在C語言中
在C語言中,const修飾的變量不具有常量的特性,只是一個不可修改的變量,實質上仍然是變量,在編譯期間無法知道它的值,不可以用作數組下標。
2.在C++中
在C++中,const就有很大不一樣,C++中鼓勵使用const來替代#define,在C++中對const定義的變量分為兩種情況:
情況1(在.rodata段分配空間):
如果const用在全局或者使用了static關鍵字說明,例如extern const int i=10,static const int i=10。那麼這個i就是一個常量(網上有人說真正意義上的常量),並且該常量是存放在.rodata段的,是無法通過取地址方式去修改的(具體情況見情況2),修改會報段錯誤。
情況2(不在.rodata段分配空間):
如果const用在局部並且沒有使用static關鍵字,例如在main函數裡面const int i=10*2+1,在這種情況下,如果對該常量:1)賦值是常量表達式(沒有其它變量或者需要外界輸入的值);2)不對該常量進行一些取地址類似的操作(&),就不會對i分配空間,僅僅是將其放在符號表中;否則,就會對i分配空間,而且這個空間是在棧上的,不同於全局的是分配在.rodata段上的。另外,注意,利用編譯器反匯編的時候,可能會有一些假象,例如,在DEBUG模式下面的反匯編,編譯器生成匯編代碼為了能更好的調試,所以無論如何都會分配空間給const常量,但是如果你打開編譯器的O2優化選項的話,就不會分配空間了。
不過,不論分不分配空間,只要該常量的賦值是常量表達式(沒有其它變量或者需要外界輸入的值),編譯器都會做一個優化,叫做常量折疊(const folding),簡單來說,就是編譯的時候,任何用到i的地方,都會直接用21去替換i。
a.有常量折疊
如果i的賦值是常量表達式(沒有其它變量或者需要外界輸入的值),即i的值不需要訪問存儲空間來確定,那麼程序中任何出現i的地方就已經在編譯期間被替換,即使通過取地址來修改這個值,也是相當於改變了一個副本而已,如下:
const int i=1;
int *p=(int *)&i;
*p=2;
cout<<*p<<endl<<i<<endl;
輸出結果:
2
1
這說明,i的值是確定的情況下,程序中任何出現i的地方都被1替換了。
b.無常量折疊
另一種情況,如果賦值不是常量表達式,這時是需要訪問存儲區域才能得到確切值的,這種情況並不會有"常量折疊",如下:
int i=10;
const char gc = cin.get(); //或者const char gc = i都是一樣的
char *t = (char *)&gc;
*t += 2;
cout << *t <<endl << gc << endl;
輸如:a
輸出:
c
c
從上面這個情況可以看出,雖然gc是局部的const,但是它的值是不確定的,是需要用戶輸入的,因此會為gc在棧中分配空間,並且因為gc的值不確定,它不能得到常量折疊帶來的優化。程序中使用gc的地方,必須直接到存儲區域去訪問才能得到值,所以*t和gc的值是保持一致的。
無常量折疊的情況下,一定是分配了內存空間的,因為無常量折疊的本質就是值不確定,需要到內存中獲取。而有常量折疊的情況下,內存空間可能分配了,也可能沒分配,如果分配了,那是因為對該常量進行了取地址相關的操作。
volatile關鍵字:
除此之外,volatile關鍵字也能夠屏蔽掉常量折疊,如下:
volatile const int i=1;
int *p=(int *)&i;
*p=2;
cout<<*p<<endl<<i<<endl;
輸出結果:
2
2
上面是我查閱資料綜合得到的情況,具體來說,const使用情況可分為全局和局部(static關鍵字是一個因素),局部下又可分為分配內存與不分配內存、有常量折疊和無常量折疊,分不分配內存取決於是否要對該常量進行地址相關的操作(如&或者直接從內存取值),有沒有常量折疊取決於需不需要直接從內存中取值。
有很多不足還請各位大神指出,大家相互學習相互進步,勿噴就好,謝謝啦
轉載請說明出處