天天看點

C++和C的const差別

c和c++關于const的一些差別

以下參考了網上的一些資料并通過程式驗證。

注意,以下情況都是用gcc和g++編譯器得到的結果,用vs編譯器又會有所不同。

以下說下c和c++中const定義的常量的一些差別:

c++中用const定義了一個常量後,不會配置設定一個空間給它,而是将其寫入符号表(symbol table),這使得它成為一個編譯期間的常量,沒有了存儲與讀記憶體的操作,使得它的效率也很高。但是const定義的常量本質上也是一個變量,是變量就會有位址,那麼什麼時候會配置設定記憶體?看看下面的代碼:

int main(){
    const int a  = 2;
    int* p = (int*)(&a);
    *p = 30;
    cout<<&a<<endl; 
    cout<<p<<endl;  
    cout<<a<<endl;
    cout<<*p<<endl;
}
           

我們看到,通過 intp = (int)(&a);這種方法,可以直接修改const常量對應的記憶體空間中的值,但是這種修改不會影響到常量本身的值,因為用到a的時候,編譯器根本不會去進行記憶體空間的讀取。這就是c++的常量折疊(constant folding),即将const常量放在符号表中,而并不給其配置設定記憶體。編譯器直接進行替換優化。除非需要用到a的存儲空間的時候,編譯器迫不得已才會配置設定一個空間給a,但之後a的值仍舊從符号表中讀取,不管a的存儲空間中的值如何變化,都不會對常量a産生影響。

但是在c中卻不是這樣.c沒有constant folding的概念,用constant定義一個常量的時候,編譯器會直接開辟一個記憶體空間存放該常量。不會進行優化。同樣的例子在c下面會産生不同的結果:

```cpp
 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 }
           

我們看到,在c裡面,一個被const定義為常量的值,堂而皇之地被修改了,而且編譯器沒有報任何錯誤 !

如果我們進一步深入可以發現,對于以上兩個例子來說,a都是定義在某個函數之内的(比如main()函數),不管是c還是c++,本質上都隻是将其當成一個普通的局部變量來對待,都隻是在棧上配置設定空間。是以const根本就不能起到阻止修改其記憶體空間的作用,一個合法的強制類型轉換就可以輕松搞定。c++比c好的地方就在于使用了constant folding的機制,使得常量的值跟對應的記憶體空間無關,進而保護了該常量值。

以上的例子是針對局部的const常量而言

對全局的const變量,c++仍舊采用constant folding政策,故以下代碼是行得通的:

//global variable

const int a = 3;

int arr[a];

但是c會報錯: error: variably modified ‘arr’ at file scope, 原因在于gcc認為a隻是一個普通的全局變量,而變量是不能用來指定數組的長度的。當然,這是針對全局數組而言。

如果是局部的數組的話,就算是int a = 3; int arr[a];這種都是可以的,因為c裡面還有一種叫變長數組的東西(我暈~,貌似因為兩者的實作機制不一樣,這個要再看看)

另外,對于全局a,在c和c++中如果我們仍然用int p = (int)(&a);這種方法來修改它記憶體中的值,編譯時不會報錯,但是運作時會報段錯誤,因為a是放在隻讀的全局資料區中,修改該區中的資料會引發段錯誤。

在vs編譯器下:

1.不支援變長數組,一個變量除非被聲明為const,否則不能用來聲明數組的長度。

2.const變量,不管是全局的還是局部,都是放在隻讀資料區,是以無法用前面的方法來修改記憶體空間裡面的值,編譯時就會報錯。

文章來源:https://www.cnblogs.com/fnlingnzb-learner/p/9282962.html

繼續閱讀