深入聊一下 Const 關(guān)鍵字
const是一個(gè)C語言的關(guān)鍵字,它限定一個(gè)變量不允許被改變。使用const在一定程序上可以提高程序的健壯性,另外,在觀看別人代碼的時(shí)候,清晰理解const所起的作用,對(duì)理解別人的程序有所幫助。
01const簡述
下面簡單描述一下const,基本都是教科書的知識(shí)。const修飾的變量,其值存放在只讀數(shù)據(jù)段中,其值不能被改變。稱為只讀變量。關(guān)于什么是數(shù)據(jù)段,什么是代碼段,請(qǐng)看我之前的文章《C語言的內(nèi)存分配》。
int const a;
const int a;
上面兩條語句都可以將a聲明為一個(gè)整數(shù),它的值不能被修改。這兩種方式你可以任意選一種即可。
常量在定義時(shí)可以被初始化。
int const a =15;
當(dāng)指針和常量結(jié)合時(shí),就會(huì)很有趣,因?yàn)橛袃蓸訓(xùn)|西都可能成為常量,指針和它指向的實(shí)體。一般大家在大學(xué)考計(jì)算機(jī)二級(jí)和面試時(shí)經(jīng)常會(huì)遇到的。
int *a;
a就是一個(gè)很普通的指向整型的指針。
int const *a;
這時(shí)則是一個(gè)指向整型常量的指針。也就是說,你可以修改指針的值,但是不能修改它指向的值。
int *const a;
這時(shí)a是一個(gè)指向整型的常量指針。這個(gè)指針是常量,它的值無法修改,但是你可以修改它所指向的整型的值。
int const *const a;
這個(gè)時(shí)候無論是指針本身還是它所指向的值都是常量,都不允許修改。
那么問題來了,就像C語言的運(yùn)算符的優(yōu)先級(jí),這個(gè)東西很不好記憶,在實(shí)際開發(fā)中,我們直接多用()符號(hào)解決優(yōu)先級(jí)的問題。上面指針和const結(jié)合那么麻煩,學(xué)習(xí)為了什么呢?
1、合理地使用關(guān)鍵字const可以使編譯器很自然地保護(hù)那些不希望被改變的參數(shù),防止其被無意的代碼修改。簡而言之,這樣可以減少bug的出現(xiàn)。
2、正是基于上面的原因,一些優(yōu)秀的開源代碼都會(huì)利用const這個(gè)屬性,深入理解后,方便我們閱讀理解一些優(yōu)秀的開源代碼。
02常量的應(yīng)用
上文就簡述了一下教科書中的const定義,現(xiàn)在說一下const在我日常開發(fā)中的應(yīng)用。
在單片機(jī)開發(fā)中
const定義一個(gè)常量,在單片機(jī)開發(fā)中,一個(gè)定義在函數(shù)體外的常量constint a = 5; 它是存儲(chǔ)在單片機(jī)內(nèi)部Flash里的,不懂的同學(xué)請(qǐng)看之前的文章《C語言在STM32中的內(nèi)存分配》。那么上文提到和指針結(jié)合時(shí),也是存儲(chǔ)在內(nèi)部Flash中嗎?我們來驗(yàn)證一下
int data = 0x1234;
int const *a = &data;
int *const b= &data;
int const *const c= &data;
int main(void)
{
int data1 = 0x1234;
a =&data1;
data1 = *b;
data1 = *c;
while(1);
}
它們的內(nèi)存分配如下
b和c是分配到內(nèi)部flash的,a是分配到ram中的。其實(shí)這也很好理解,根據(jù)上面的const的定義,單片機(jī)在分配時(shí),將不能修改的變量,也就是只讀變量放到flash中,可以讀寫的變量放到ram中,這個(gè)大家仔細(xì)想一下就明白了。
常量作為函數(shù)的參數(shù)
非指針參數(shù)(也就是傳值參數(shù))不會(huì)被修改原始值,const對(duì)它是沒有意義的,所以這里只討論參數(shù)是指針加const的情況。
在上面看到,指針加const共3種情況,這里先討論int const *a; 也就是你可以修改指針的值,但是不能修改它指向的值。
int fun(int *p)
{
if(*p == 0xA5)
{
return*p;
}else{
p++;
return *p;
}
}
上面是個(gè)簡單的例子,也就是傳入一個(gè)指針,函數(shù)讀取指針指向的內(nèi)容,執(zhí)行不同的命令。類似串口接收,一個(gè)函數(shù)內(nèi)部處理這些數(shù)據(jù),但是不能修改,可能串口接收的數(shù)據(jù)在其他地方還有用。
在上述例子中,沒有問題的,因?yàn)榇a全在“掌控”中。函數(shù)內(nèi)部是否進(jìn)行寫操作,自己是知道的。但還有一個(gè)更規(guī)范的寫法。
int fun(int const *p)
{
if(*p == 0xA5)
{
return *p;
}else{
p++;
return *p;
}
}
這里寫法,就是明顯表現(xiàn)出自己的設(shè)計(jì)意圖,函數(shù)內(nèi)部不可以對(duì)指針指向的內(nèi)容進(jìn)行修改,只能讀取。
如果嘗試修改,編譯器會(huì)直接報(bào)錯(cuò)的。但是函數(shù)內(nèi)部也是可以繞過去,修改的數(shù)據(jù)的,如下
int fun(const int *p)
{
int *p2 = p; /* 來個(gè)重名指針會(huì)繞過const的限制*/
*p2 += 1;
return*p;
}
那么對(duì)于int *const a;有沒有對(duì)應(yīng)的使用場景呢?如下
這樣的接口設(shè)計(jì),如果函數(shù)內(nèi)部嘗試修改指針的值,也就是指針指向的位置,編譯器就會(huì)直接報(bào)錯(cuò)。
不過這里例子很現(xiàn)實(shí),因?yàn)榧词谷サ魀2的const修飾,編譯器會(huì)直接報(bào)waring,因?yàn)閜2是入?yún)ⅰ_@里只是簡單舉例子,大家理解意思就好。
在日常開發(fā)中,入?yún)⑹莍ntconst *a; 使用場景比較多。
C++中應(yīng)用加const
C++中可以使用應(yīng)用的語法,這里不再展開什么是應(yīng)用,如下例子,C++函數(shù)參數(shù)中引用時(shí)也常加const修飾,如下
void find(constint &x)
{
.......
}
最后,舉兩個(gè)常用的標(biāo)準(zhǔn)C庫函數(shù)聲明,它們都是使用const的典范。
1.字符串拷貝函數(shù):char*strcpy(char*strDest,constchar *strSrc);
2.返回字符串長度函數(shù):intstrlen(constchar *str);
03#define和const
#define預(yù)編譯和const在某些情況下有些“混淆”,如下
#define MAX_NUM 5
int const max_num = 5;
void fun(){
if(len >MAX_NUM)
if(len> max_num)
}
上述代碼5行和6行都能起效果。那么我們就詳細(xì)分析一下它們的區(qū)別
1、#define的數(shù)據(jù)是宏定義,它占用的是代碼段空間(單片機(jī)對(duì)應(yīng):內(nèi)部flash),const定義一個(gè)數(shù)據(jù)類型,它占用的是data段(單片機(jī)對(duì)應(yīng):內(nèi)部ram)。
2、如上,#define是宏定義,在預(yù)編譯階段直接替換,而const是數(shù)據(jù)類型。
#define MAX_NUM 5
int const max_num = 5;
int data[MAX_NUM];
intdata2[max_num];
上述代碼第4行是編譯不過的,因?yàn)閙ax_num是一個(gè)int的數(shù)據(jù)類型變量,數(shù)組定義的長度不能用變量。實(shí)際上,在更章節(jié)第一個(gè)例子,只用于判斷長度,#define更加合適,因?yàn)橹灰试S使用字面值常量的地方都可以使用宏定義。
3、define只是簡單的字符串替換,沒有類型檢查。而const有對(duì)應(yīng)的數(shù)據(jù)類型,是要進(jìn)行判斷的,可以避免一些低級(jí)的錯(cuò)誤。define只是簡單的字符串替換會(huì)導(dǎo)致邊界效應(yīng),
比如定義
#define A 1
#define B A+3
#define C A/B3
那么c是多少呢?c=A/B3=A/A+33=1/1+33=10;所以這種用的時(shí)候可以直接都用個(gè)括號(hào)括起來,就不怕邊界效應(yīng)了。
4、const不能重定義,不可以定義兩個(gè)一樣的,而define就比較牛氣了,它通過undef取消某個(gè)符號(hào)的定義,再重新定義。并還可以用于判斷宏定義是否存在,常用于頭文件防止頭文件被重復(fù)引用。
#ifndef GRAPHICS_H //防止graphics.h被重復(fù)引用
#defineGRAPHICS_H
……代碼……
#endif
5、const常量可以進(jìn)行調(diào)試的,define是不能進(jìn)行調(diào)試的,主要是預(yù)編譯階段就已經(jīng)替換掉了,調(diào)試的時(shí)候就沒它了。
本文轉(zhuǎn)載自微信公眾號(hào)「知曉編程」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系知曉編程公眾號(hào)。