提到 const 都知道是修飾常量的,在一個變量前加上const 關鍵字后這個常量就不可以再賦值了!
C語言中不是有#define嗎,干嘛還要用const呢,我想事物的存在一定有它自己的道理,所以說const的存在一定有它的合理性,與預編譯指令相比,const修飾符有以下的優點:
1、預編譯指令只是對值進行簡單的替換,不能進行類型檢查
2、可以保護被修飾的東西,防止意外修改,增強程序的健壯性
3、編譯器通常不為普通const常量分配存儲空間,而是將它們保存在符號表中,這使得它成為一個編譯期間的常量,沒有了存儲與讀內存的操作,使得它的效率也很高。
(以上三點摘抄自:https://blog.csdn.net/xingjiarong/article/details/47282255)
const 的應用
1 定義常量
const int a=5;
int const a=5;
兩種用法是一樣的,這里變量 a 在第一次定義時復制后,在程序運行中就不可再賦值改變了;
例如:
int main (int argc,char* argv)
{
const int a=5;
int const b=5;
a=3; //編譯時將會報錯,常量不可修改
b=8;//編譯時將會報錯,常量不可修改
}
const用於修飾常量靜態字符串,
例如:
const char* Str="ABCDEFGH";
此時 const修飾過的str就是常量 我們不可更改Str的值 如 Str[3]='H'; 這時候是錯誤的,
2 常量指針與指針常量
很多人往往分不清這兩者的形態,
常量指針:
const int* pv;
int const* pv;
兩種定義方式一樣,都是定義一個常量指針;即不可通過這個指針修改所指向地址的值;但是所指向的地址的值是可以通過其他變量指針修改的;
但是常量指針可以賦值新的指向地址;
例如:
int main (int argc,char* argv)
{
int a=5;
int b=7;
const int* m=&a;
int const* n=&b;
int* p = n;//把常量指針 n 指向的地址賦給p;
*m=3; //編譯時將會報錯,常量指針不可修改所指向的地址的值
*n=8;//編譯時將會報錯,常量指針不可修改所指向的地址的值
*p = 9; //編譯無措,可以通過變量指針修改常量指針所指向的地址的值
m=&b; //編譯無措,常量指針可以修改所指向的地址
n=&a; //編譯無措,常量指針可以修改所指向的地址
}
指針常量
int* const pv;
是指這個指針指向的地址不可在改變,但指向的地址的值可以再改變;(指針常量是指指針本身是個常量,不能在指向其他的地址)
int main(int argc,char* argv)
{
int a=5;
int b=7;
int* const m=&a;
*m =8; //編譯無措 指針常量可以通過該指針修改所指向的地址的值
m = &b; //編譯出錯 指針常量不可修改所指向想的地址
}
指向常量的常指針
常量指針結合指針常量 即指向常量的常指針 表示指針本身和指針所指向的地址在定義時賦值后都不可再改變;
定義如下:
const int* const p;
那么如何來區分常量指針和指針常量呢?
這就要明白關鍵字的結合優先級了,
如:const int* p;
以*優先級最高,先和int 結合 得到 " int* " (讀作整形指針) 然后(int*)和 const 結合得到 " const(int*)" (讀作常量指針) ,然后才和p結合得到"(const(int*))p" (讀作常量指針p),
int* const p;
同理,以*優先級最高,先和int結合得到"int*"(讀作整形指針),然后(int*)和const結合得到"(int*)(const)"(讀作指針常量),最后才和p結合得到"(int*)(const)p"(讀作指針常量p)
3 常量函數
常見的定義方式
class AA
{
public:
void mf_Fun1()
{
int b=10;
num=b;
}
void mf_Fun2() const
{
cout<<num; //編譯無措,只讀取成員變量
num+=15; //錯誤 const 函數不可修改其成員變量,只可讀取
}
}
int main()
{
AA a1;
const AA a2; //注意這里的const關鍵字
a2.mf_Fun2();
a2.mf_Fun1(); // 錯誤,const的實例對象 不能訪問非const的函數
}
在類成員函數的聲明和定義中,
const的函數不能對其數據成員進行修改操作。
const的對象,不能引用非const的成員函數。
這兒的const就是說這個函數操作不會對變量或是對象之類的值有影響 比如、有一個human類 ,
現在要得到某個human類對象A的age 那么肯定是不會因為想得到這個值而改變了age的大小,
那么就可以寫一個函數int getAge()const這樣就好 這么做是為了防止在函數中對不應該在這里改變的量不小心進行了改變
(抄錄自 https://zhidao.baidu.com/question/1702736835898870060.html)
4 在什么情況下需要用到Const關鍵字?
4.1 修飾函數的參數
根據常量指針與指針常量,const修飾函數的參數也是分為三種情況
1、防止修改指針指向的內容
void StringCopy(char *strDestination, const char *strSource);
其中 strSource 是輸入參數,strDestination 是輸出參數。給 strSource 加上 const 修飾后,如果函數體內的語句試圖改動 strSource 的內容,編譯器將指出錯誤。
2、防止修改指針指向的地址
void swap ( int * const p1 , int * const p2 )
指針p1和指針p2指向的地址都不能修改。
3、以上兩種的結合。
4.2 修飾函數的返回值
如果給以“指針傳遞”方式的函數返回值加 const 修飾,那么函數返回值(即指針)的內容不能被修改,該返回值只能被賦給加const 修飾的同類型指針。
例如函數
const char * GetString(void);
如下語句將出現編譯錯誤:
char *str = GetString();
正確的用法是
const char *str = GetString();
4.3 修飾全局變量
全局變量的作用域是整個文件,我們應該盡量避免使用全局變量,因為一旦有一個函數改變了全局變量的值,它也會影響到其他引用這個變量的函數,
導致除了bug后很難發現,如果一定要用全局變量,我們應該盡量的使用const修飾符進行修飾,這樣防止不必要的人為修改,使用的方法與局部變量是相同的。
4.4 寄存器變量定義和寄存器讀取
例如:
uint32_t* R0 =(uint32*)0x400F00FF; //定義一個地址為0x400F00FF的32bit寄存器變量
正確的定義方法:
uint32_t* const R0 =(uint32*)0x400F00FF; //定義一個指針常量R0指向地址為0x400F00FF的寄存器 這樣就保證變量R0指向的地址的唯一性,
若是指向一個只讀寄存器則應該按如下定義:
const uint32_t* const R0 =(uint32*)0x400F00FF; //定義一個指向常量的常指針 R0 指向地址為0x400F00FF的只讀寄存器,這樣就保證變量R0指向的地址的唯一性,同時不會因操作該指針修改指向地址的值