const在C與C++中的區別
在C中,const不是常量,只能說是一個不能改變的變數(注意是變數),C編譯器不能把const看成看成一個編譯期間的常量,因為他在記憶體中有分配,C編譯器不知道他在編譯期間的值。所以不能作為陣列定義時的下標,因為它必須為常量。
在C中,const int a;是可以的,因為這只是宣告一個變數,告訴編譯器,我這裡是宣告,指明在別的地方有記憶體分配。但在C++中這樣寫是不正確的,C++中const預設是內部連結,C中預設是外部連結,為了起到和c語言一樣的效果,C++需要將const修飾為extern,因為extern優先順序高於const,所以變數會被變為extern外部連結,起到和C語言一樣的效果。[
C++中是否為const分配記憶體空間要看具體情況,如果被宣告為extern或者取const變數地址,就需要為const變數分配空間。
當在自己的檔案中使用const的時候,C++不會為const常量分配空間,因為這是一種優化措施,沒有必要浪費空間去儲存一個常量,此時const int a=5 就相當於#define a 5,當在其他檔案使用的時,需要分配記憶體,同樣在程式內部引用的時候,也需要分配記憶體,因為這兩者都是採用定址的技術去使用的,不分配記憶體就沒有地址。C++中定義常量的時候不再採用define,因為define只做簡單的巨集替換,並不提供型別檢查。
C++中用const定義了一個常量後,不會分配一個空間給它,而是將其寫入符號表(symbol table),這使得它成為一個編譯期間的常量,沒有了儲存與讀記憶體的操作,使得它的效率也很高。但是const定義的常量本質上也是一個變數,是變數就會有地址,那麼什麼時候會分配記憶體?
1 int main() 2 { 3 const int a = 2; 4 int* p = (int*)(&a); 5 *p = 30; 6 cout << &a << endl; 7 cout << p << endl; 8 cout << a << endl; 9 cout << *p << endl; 10 return 0; 11 } 12 13 /*執行結果: 14 ----------- 15 010FF958 16 010FF958 17 2 18 30 19 ----------- 20 */
通過 int*p = (int*)(&a);這種方法,可以直接修改const常量對應的記憶體空間中的值,但修改不會影響到常量本身的值,因為用到a的時候,編譯器根本不會去進行記憶體空間的讀取。這就是C++的常量摺疊constant folding,即將const常量放在符號表中,而並不給其分配記憶體。編譯器直接進行替換優化。除非需要用到a的儲存空間的時候,編譯器迫不得已才會分配一個空間給a,但之後a的值仍舊從符號表中讀取,不管a的儲存空間中的值如何變化,都不會對常量a產生影響。
但是在C語言中卻不是這樣,C沒有constant folding的概念,用const定義一個常量的時候,編譯器會直接開闢一個記憶體空間存放該常量。不會進行優化。同樣的例子在C下面會產生不同的結果:
1 int main() 2 { 3 const int a = 2; 4 int* p = (int*)(&a); 5 *p = 30; 6 printf("%x\n",&a); 7 printf("%x\n",p); 8 printf("%i\n",a); 9 printf("%i\n",*p); 10 return 0; 11 } 12 13 /*執行結果 14 ------------ 15 61fe14 16 61fe14 17 30 18 30 19 ------------*/
C中,一個被const定義為常量的值,卻能被修改,而且編譯器不報任何錯誤 。進一步深入可發現,對於以上兩個例子來說,a都是定義在某個函式之內的(比如main()函式),不管是C還是C++,本質上都只是將其當成一個普通的區域性變數來對待,都只是在棧上分配空間。所以const根本就不能起到阻止修改其記憶體空間的作用,一個合法的強制型別轉換就可以輕鬆搞定。C++比C好的地方就在於使用了constant folding的機制,使得常量的值跟對應的記憶體空間無關,從而保護了該常量值。
以上的例子針對區域性的const常量而言,對全域性的const變數,C++仍舊採用constant folding策略,故以下程式碼是行得通的:1 const int a = 3; 2 int arr[a];
但C會報錯: error: variably modified 'arr' at file scope, 原因在於GCC認為a只是一個普通的全域性變數,而變數是不能用來指定陣列的長度的,這是針對全域性陣列而言。但如果是區域性的陣列的話,就算是int a = 3; int arr[a];這種都是可以的,若在C和C++中如果我們仍然用int *p = (int*)(&a);這種方法來修改它記憶體中的值,編譯時不會報錯,但是執行時會報錯誤,因為a是放在只讀的全域性資料區中,修改該區中的資料會引發段錯誤。