1. 程式人生 > >const在C與C++中的區別

const在C與C++中的區別

  在C中,const不是常量,只能說是一個不能改變的變數(注意是變數),C編譯器不能把const看成看成一個編譯期間的常量,因為他在記憶體中有分配,C編譯器不知道他在編譯期間的值。所以不能作為陣列定義時的下標,因為它必須為常量。

  在C中,const int a;是可以的,因為這只是宣告一個變數,告訴編譯器,我這裡是宣告,指明在別的地方有記憶體分配。但在C++中這樣寫是不正確的,C++中const預設是內部連結,C中預設是外部連結,為了起到和c語言一樣的效果,C++需要將const修飾為extern,因為extern優先順序高於const,所以變數會被變為extern外部連結,起到和C語言一樣的效果。[

 (1) 內連線:編譯器只對正被編譯的檔案建立儲存空間,別的檔案可以使用相同的表示符或全域性變數.C/C++中內連線使用static關鍵字指定.(2) 外連線:所有被編譯過的檔案建立一片單獨儲存空間.一旦空間被建立,聯結器必須解決對這片儲存空間的引用.全域性變數和函式使用外部連線.通過extern關鍵字宣告,可以從其他檔案訪問相應的變數和函式. ] 通俗的講:在c++ 中const 物件預設為檔案的區域性變數。與其他變數不同,除非特別說明,在全域性作用域宣告的 const 變數是定義該物件的檔案的區域性變數。此變數只存在於那個檔案中,不能被其他檔案訪問。通過指定 const 變更為 extern,就可以在整個程式中訪問 const 物件。

  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是放在只讀的全域性資料區中,修改該區中的資料會引發段錯誤。