const是一個C語言的關鍵字,它限定一個變量不容許被改變。使用const在必定程度上能夠提升程序的安全性和可靠性,另外,在觀看別人代碼的時候,清晰理解const所起的做用,對理解對方的程序也有一些幫助。
咱們來分狀況看語法上它該如何被使用。
一、函數體內修飾局部變量。
例:
void func()
{
const int a=0;
}
首先,咱們先把const這個單詞忽略不看,那麼a是一個int類型的局部自動變量,咱們給它賦予初始值0。而後再看const.
const做爲一個類型限定詞,和int有相同的地位。
const int a;
int const a;
是等價的。因而此處咱們必定要清晰的明白,const修飾的對象是誰,是a,和int沒有關係。const 要求他所修飾的對象爲常量,不可被改變,不可被賦值,不可做爲左值(l-value)。
這樣的寫法也是錯誤的。
const int a;
a=0;
這是一個很常見的使用方式:
const double pi=3.14;
在程序的後面若是企圖對pi再次賦值或者修改就會出錯。
而後看一個稍微複雜的例子。
const int* p;
仍是先去掉const 修飾符號。
注意,下面兩個是等價的。
int* p;
int *p;
其實咱們想要說的是,*p是int類型。那麼顯然,p就是指向int的指針。
同理
const int* p;
其實等價於
const int (*p);
int const (*p);
即,*p是常量。也就是說,p指向的數據是常量。
因而
p+=8; //合法
*p=3; //非法,p指向的數據是常量。
那麼如何聲明一個自身是常量指針呢?方法是讓const儘量的靠近p;
int* const p;
const右面只有p,顯然,它修飾的是p,說明p不可被更改。而後把const去掉,能夠看出p是一個指向 int形式變量的指針。
因而
p+=8; //非法
*p=3; //合法
再看一個更復雜的例子,它是上面兩者的綜合
const int* const p;
說明p本身是常量,且p指向的變量也是常量。
因而
p+=8; //非法
*p=3; //非法
const 還有一個做用就是用於修飾常量靜態字符串。
例如:
const char* name="David";
若是沒有const,咱們可能會在後面有意無心的寫name[4]='x'這樣的語句,這樣會致使對只讀內存區域的賦值,而後程序會馬上異常終止。有了 const,這個錯誤就能在程序被編譯的時候就當即檢查出來,這就是const的好處。讓邏輯錯誤在編譯期被發現。
const 還能夠用來修飾數組
const char s[]="David";
與上面有相似的做用。
二、在函數聲明時修飾參數
來看實際中的一個例子。
NAME
memmove -- copy byte string
LIBRARY
Standard C Library (libc, -lc)
SYNOPSIS
#include <string.h>
void *
memmove(void *dst, const void *src, size_t len);
這是標準庫中的一個函數,用於按字節方式複製字符串(內存)。它的第一個參數,是將字符串複製到哪裏去(dest),是目的地,這段內存區域必須是可寫。它的第二個參數,是要將什麼樣的字符串複製出去,咱們對這段內存區域只作讀取,不寫。因而,咱們站在這個函數本身的角度來看,src 這個指針,它所指向的內存內所存
儲的數據在整個函數執行的過程當中是不變。因而src所指向的內容是常量。因而就須要用const修飾。例如,咱們這裏這樣使用它。
const char* s="hello";
char buf[100];
memmove(buf,s,6); //這裏其實應該用strcpy或memcpy更好
若是咱們反過來寫,
memmove(s,buf,6);
那麼編譯器必定會報錯。事實是咱們常常會把各類函數的參數順序寫反。事實是編譯器在此時幫了咱們大忙。若是編譯器靜悄悄的不報錯,(在函數聲明處去掉const便可),那麼這個程序在運行的時候必定會崩潰。
這裏還要說明的一點是在函數參數聲明中const通常用來聲明指針而不是變量自己。
例如,上面的size_t len,在函數實現的時候能夠徹底不用更改len的值,那麼是否應該把len也聲明爲常量呢?能夠,能夠這麼作。咱們來分析這麼作有什麼優劣。
若是加了const,那麼對於這個函數的實現者,能夠防止他在實現這個函數的時候修改不須要修改的值(len),這樣很好。
可是對於這個函數的使用者,
1。這個修飾符號毫無心義,咱們能夠傳遞一個常量整數或者一個很是量整數過去,反正對方得到的只是咱們傳遞的一個copy。
2。暴露了實現。我不須要知道你在實現這個函數的時候是否修改過len的值。
因此,const通常只用來修飾指針。
再看一個複雜的例子
int execv(const char *path, char *const argv[]);
着重看後面這個,argv.它表明什麼。
若是去掉const,咱們能夠看出
char * argv[];
argv是一個數組,它的每一個元素都是char *類型的指針。
若是加上const.那麼const修飾的是誰呢?他修飾的是一個數組,argv[],意思就是說這個數組的元素是隻讀的。那麼數組的元素的是什麼類型呢?是char *類型的指針.也就是說指針是常量,而它指向的數據不是。
因而
argv[1]=NULL; //非法
argv[0][0]='a'; //合法
三、全局變量。
咱們的原則依然是,儘量少的使用全局變量。咱們的第二條規則則是,儘量多的使用const。若是一個全局變量只在本文件中使用,那麼用法和前面所說的函數局部變量沒有什麼區別。若是它要在多個文件間共享,那麼就牽扯到一個存儲類型的問題。
有兩種方式。
1.使用extern
例如
extern const double pi;
const double pi=3.14;
而後其餘須要使用pi這個變量的,包含file1.h
#include "file1.h"
或者,本身把那句聲明覆制一遍就好。
這樣作的結果是,整個程序連接完後,全部須要使用pi這個變量的共享一個存儲區域。
2.使用static,靜態外部存儲類
static const pi=3.14;
須要使用這個變量的*.c文件中,必須包含這個頭文件。
前面的static必定不能少。不然連接的時候會報告說該變量被屢次定義。這樣作的結果是,每一個包含了constant.h的*.c文件,都有一份該變量本身的copy,該變量實際上仍是被定義了屢次,佔用了多個存儲空間,不過在加了static關鍵字後,解決了文件間重定義的衝突。壞處是浪費了存儲空間,致使連接完後的可執行文件變大。可是一般,這個,小小几字節的變化,不是問題。好處是,你不用關心這個變量是在哪一個文件中被初始化的。
最後,說說const的做用。
const 的好處,是引入了常量的概念,讓咱們不要去修改不應修改的內存。直接的做用就是讓更多的邏輯錯誤在編譯期被發現。因此咱們要儘量的多使用const。可是不少人並不習慣使用它,更有甚者,是在整個程序編寫/調試 完後才補const。若是是給函數的聲明補const,尚好。若是是給全局/局部變量補const,那麼……那麼,爲時已晚,無非是讓代碼看起來更漂亮了。