C++解析(2):進化後的 const 分析
0.目錄
1.C語言中的const
2.C++中的const
3.對比
- 3.1 C語言與C++中的const
- 3.2 C++中的const與巨集定義
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常量的語法特性