1. 程式人生 > >C++解析(2):進化後的 const 分析

C++解析(2):進化後的 const 分析

0.目錄

1.C語言中的const

2.C++中的const

3.對比

4.小結

1.C語言中的const

  • const修飾的變數是只讀的,本質還是變數
  • const修飾的區域性變數在棧上分配空間
  • const修飾的全域性變數在只讀儲存區分配空間
  • const只在編譯期有用,在執行期無用

也就是說:const修飾的變數不是真的常量,它只是告訴編譯器該變數不能出現在賦值符號的左邊。
(如果我們修改了const修飾的全域性變數,那麼程式將產生一個崩潰,因為我們修改了只讀儲存區裡面的內容,那麼大多數編譯器所編譯出來的可執行程式都會發生崩潰的。)

  • C語言中的const使得變數具有只讀屬性
  • const將具有全域性生命週期的變數儲存於只讀儲存區

綜上,const不能定義真正意義上的常量!
(面試題:在C語言裡面,你有辦法定義真正的常量嗎?
如果回答:可以,用const。//C語言一般
因為在C語言裡面只有通過enum來定義的這些識別符號才是真正意義上的常量,也就是說,C語言裡面真正意義上的常量只有列舉。const只不過說使得變數具有了只讀屬性而已,它並不能定義真正意義上的常量。)

那麼C++中的const到底有什麼改變呢?
這是test.c程式碼:

// test.c
#include <stdio.h>

int main()
{
    const int c = 0;
    int* p = (int*)&c;
    
    printf("Begin...\n");
    
    *p = 5;
    printf("c = %d\n", c);
    printf("*p = %d\n", *p);
    
    printf("End...\n");
    return 0;
}

這是test.cpp程式碼:

// test.cpp
#include <stdio.h>

int main()
{
    const int c = 0;
    int* p = (int*)&c;
    
    printf("Begin...\n");
    
    *p = 5;
    printf("c = %d\n", c);
    printf("*p = %d\n", *p);
    
    printf("End...\n");
    return 0;
}

分別用gcc編譯器和g++編譯器執行程式結果如下:

[[email protected]
Desktop]# gcc test.c [[email protected] Desktop]# ./a.out Begin... c = 5 *p = 5 End... [[email protected] Desktop]# g++ test.cpp [[email protected] Desktop]# ./a.out Begin... c = 0 *p = 5 End...

2.C++中的const

C++在C的基礎上對const進行了進化處理

  • 當碰見const宣告時在符號表中放入常量
  • 編譯過程中若發現使用常量則直接以符號表中的值替換
  • 編譯過程中若發現下述情況則給對應的常量分配儲存空間:

    • const常量使用了extern
    • const常量使用&操作符

注意:C++編譯器雖然可能為const常量分配空間,但不會使用其儲存空間中的值。
(為什麼會有這種行為?這是為了相容C語言。相容的意思是說,之前能用C語言編譯器編譯的程式,用C++編譯器也要能編譯通過。只不過說編譯通過了以後,可能可執行程式執行的結果會有所不同。)


(符號表就是編譯器在編譯的過程當中所產生的一張表,這張表是編譯器內部的資料結構,也就是說,編譯器在編譯程式的時候會產生各種各樣的資料結構,這些內部的資料結構裡面就有一個叫做符號表。)

3.對比

3.1 C語言與C++中的const

C語言中const變數是只讀變數,會分配儲存空間

C++中的const可能分配儲存空間

  • const變數為全域性,並且需要在其它檔案中使用
  • 當使用&操作符對const常量取地址

這是test.c程式碼:

// test.c
#include <stdio.h>

void f()
{
    #define a 3
    const int b = 4;
}

void g()
{
    printf("a = %d\n", a);
    //printf("b = %d\n", b);
}

int main()
{
    const int A = 1;
    const int B = 2;
    int array[A + B] = {0};
    int i = 0;
    
    for(i=0; i<(A + B); i++)
    {
        printf("array[%d] = %d\n", i, array[i]);
    }
    
    f();
    g();
    
    return 0;
}

這是test.cpp程式碼:

// test.cpp
#include <stdio.h>

void f()
{
    #define a 3
    const int b = 4;
}

void g()
{
    printf("a = %d\n", a);
    //printf("b = %d\n", b);
}

int main()
{
    const int A = 1;
    const int B = 2;
    int array[A + B] = {0};
    int i = 0;
    
    for(i=0; i<(A + B); i++)
    {
        printf("array[%d] = %d\n", i, array[i]);
    }
    
    f();
    g();
    
    return 0;
}

分別用gcc編譯器和g++編譯器執行程式結果如下:

[[email protected] Desktop]# g++ test.cpp
[[email protected] Desktop]# gcc test.c
test.c: In function ‘main’:
test.c:20: error: variable-sized object may not be initialized
test.c:20: warning: excess elements in array initializer
test.c:20: warning: (near initialization for ‘array’)
  • C語言編譯器不能編譯通過,20行會報錯。因為定義陣列array,需要知道陣列的大小,而陣列的大小A+B是由兩個只讀變數相加得到的。兩個變數相加的結果需要到執行時才知道,因此編譯器不樂意了,編譯器根本不知道陣列大小,所以直接報錯。
  • C++編譯器直接編譯通過。因為陣列大小是由兩個真正意義上的常量A和B來完成的。這時候編譯器編譯到這個地方,顯然知道陣列的大小是3。

3.2 C++中的const與巨集定義

C++中的const常量類似於巨集定義:
const int c = 5;#define c 5

C++中的const常量與巨集定義不同

  • const常量是由編譯器處理
  • 編譯器對const常量進行型別檢查作用域檢查
  • 巨集定義由前處理器處理,單純的文字替換

這是test.cpp程式碼:

// test.cpp
#include <stdio.h>

void f()
{
    #define a 3
    const int b = 4;
}

void g()
{
    printf("a = %d\n", a);
    printf("b = %d\n", b);
}

int main()
{
    const int A = 1;
    const int B = 2;
    int array[A + B] = {0};
    int i = 0;
    
    for(i=0; i<(A + B); i++)
    {
        printf("array[%d] = %d\n", i, array[i]);
    }
    
    f();
    g();
    
    return 0;
}

用g++編譯器執行程式結果如下:

[[email protected] Desktop]# g++ test.cpp
test.cpp: In function ‘void g()’:
test.cpp:13: error: ‘b’ was not declared in this scope

巨集沒有作用域和型別的概念,但是const常量有作用域和型別的概念。兩者的本質不一樣。

4.小結

  • 與C語言不同,C++中的const不是隻讀変量
  • C++中的const是一個真正意義上的常量
  • C++編譯器可能會為const常量分配空間
  • C++完全相容C語言中const常量的語法特性