變量實際上是程序在運行的其值可以改變的內存單元的名字,而常量是在程序執行過程中其值固定不變的內存單元的名字,所以,常量必須在定義時初始化。
如果這塊數據(這個常量)從字面上看就能知道它的值,那它叫做“字面常量”。
const double PI = 3.14159; PI = 3.14; //錯誤,PI已存入常數區,不能再改變值 const int SIZE = 3; int const X = 5; //類型名和const可以互換位置 const char C = 'k'; int j = 10; constexpr int j1 = j + 10; //表達式中可以出現在常量定義語句中。 //上面都是字面常量 const char *STR = "Hello";
可以看到 const 和 constexpr 的功能基本相同,都用於定義常量,但存在以下區別:
constexpr變量必須在編譯時進行初始化,而const變量的初始化可以延遲到運行時。具體而言,用於初始化constexpr常量的表達式中的每部分紙都是程序運行之前就可以確定的字面值常量。而const無此限制,它只限定了定義的常量在程序運行期間不可被修改,但其初始值即使在運行時才能取得也是可以的。
例如,若size() 函數的功能是計算類型數據的長度,則
const int n = size(); //正確,但n的值的取得是在執行函數時 constexpr int m = size(); //錯誤,程序編譯時,不知道size()的值 const int i = 10; int j = 21; const int i1 = i + 10; //正確 const int j1 = j + 10; //正確 constexpr int i2 = i + 10; //正確,編譯時可以知道i的值等於10 constexpr int j2 = j + 10; //錯誤,j是變量
const、constexpr與指針
const可以與指針結合,由於指針涉及 “ 指針本身和指針所指的對象 ”,因此它與常量的結合也比較復雜,可分為三種情況。
- 第一種是常量指針,即指針是常量,不能被改變,但其所指內存空間是變量,可修改。
-
type *const p;//p是const ——> *p不是
-
- 第二種是指向常量的指針,即指針是變量,可再指向其他內存空間單元,但其所指單元是變量,不能修改。
-
type const *p; 或者 const type *p; //p不是const ——> *p是const
-
- 第三種是指向常量的常指針,指針及所指內存單元都是常量,都不能被修改。
-
const type *const p;//p是const ——> *p也是const
-
實例: (以下代碼在某函數內部)
const int a = 5; int b = 9; const int *pa = &a; int *const pb = &b;
在來一個完整代碼:
#include <iostream> using namespace std; int main() { //char *const p0; //錯誤,p0是常量,必須初始化 char *const p1 = "dukang"; //正確 char const *p2; //正確 const char *p3 = "dukang"; //正確 const char *const p4 = "dukang"; //正確 //const char *const p5; //錯誤,p5是常量必須初始化 //p1 = "wankang"; //錯誤,p1是常量,不可改 p2 = "wankang"; //正確,p2是變量,可改 p3 = "wankang"; //正確,p3是變量,可改 //p4 = "wankang"; //錯誤,p4是常量,不可改 p1[0] = "w"; //正確 //p2[0] = "w"; //錯誤,*p2是常量,不可改 //p3[0] = "w"; //錯誤,*p3是常量,不可改 //p4[0] = 'w'; //錯誤,*p4是常量,不可改 system("pause"); return 0; }
const 對指針和對象之間的相互賦值具有一定影響:
const對象的地址只能賦給指向const對象的指針,否則引起編譯錯誤。但指向const對象的指針可以指向const對象,也可以指向非const對象
例如:
int x = 9; const int y = 9; int *p1; const int *p2; p1 = &y; //錯誤,引起編譯錯誤,若改為 “ p1=&x; ” 則是正確。 p2 = &x; //正確 p2 = &y; //正確
用constexpr 限定指針時,會比const簡單很多,它只限制指針變量本身是常量,它與所指變量沒有關系
int x; const int *p1 = &x; //p1是普通指針,指向的對象是常量 constexpr int *p2 = &x; //而p2是常數指針,指向的對象不是常量
const與引用
在定義引用時,可以用const進行限制,使它成為不允許被修改的常量引用。
例如:
int i = 9; int &rr = i; const int &ir = i; rr = 8; ir = 7;//錯誤,ir是const引用,不允許通過它修改對應的變量i
const 引用可以用常量初始化,但非const 引用不能用常量初始化。這與編譯器的處理方式有關,編譯器在實現常量引用時生成了一個臨時對象,然后讓引用指向這個對象,但該對象對用戶而言是隱藏不可知的,不能訪問。
int i; const double &ff = 10.0; const int &ir = i + 10; int &ii = 3; //錯誤
頂層const 和底層const
- 頂層const其實是指不可被修改的常量對象,此概念可以推廣到任意類型的數據類型,它們定義的常量對象都是頂層const;
- 底層const則與指針和引用這樣的復合類型有關。所有聲明為const的引用都是底層const
- 其中指針比較特殊,既可以頂層const,也可能是底層const。
int i = 0; const double d = 9.0; //ic為頂層const const int ic = 32; //ic為頂層const const int &ri = i; //ri為底層const const int &ric = ic; //ric為底層const
指針實際上定義了兩個對象:指針本身和指針所指的對象。
- 當指針本身被限定為常量時,稱指針為頂層const;
- 當所指的對象被限定為常量,而指針本身未被限定時,稱指針為底層const;
- 當指針和所指對象兩者都被限定為常量,稱指針為頂層const,對象為底層const。
int i = 0; const int ic = 32; int *const p1 = &i; //p1為頂層const const int *p2; //p2為底層const const int *const p3 = ⁣ //p3為頂層const,(*p3)為底層const
在進行復制操作時,復制頂層const對象與底層const對象存在以下區別。
- 復制頂層const不受影響。由於執行復制時不影響被復制對象的值,因此它是否為常量對復制沒有影響。例如,對於上面的語句組,執行以下的復制操作。
-
i=ic;//正確,ic是一個頂層const,對此操作無影響 p2=p3//正確,p2和p3指向的對象類型相同,p3頂層const部分不影響
-
- 底層const的復制是受影響的。要求黏貼和復制的對象有相同的底層const或者能夠轉換為相同的數據類型,一般而言,非常量能夠轉換成常量,反之則不行。例如,對於上面的語句組,執行以下的復制操作。
-
p2 = p3; //正確,p2為底層const,p3是頂層也是底層const,且類型相同; p2 = &i; //正確,p2為底層const,&i為int*,且能轉換成const int* p2 = ⁣ //正確,p2為底層const,&ic為const int* p2 = &ri; //正確,p2的ri為相同類型的底層const int *p = p3; //錯誤,p3包括底層const定義,而p沒有 const int &r2 = i; //正確,const int&可以綁定到一個普通int上 int &r = ic //錯誤,普通的int&不能綁定到int常量上
-