(轉)話說C語言const用法

const在C語言中算是一個比較新的描述符,咱們稱之爲常量修飾符,意即其所修飾 的對象爲常量(immutable)。 咱們來分狀況看語法上它該如何被使用。數組

一、函數體內修飾局部變量。 例: 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; // 非法,a不可變io

這也是一個很常見的使用方式: const double pi=3.14;在程序的後面若是企圖對pi再次賦值或者修改就會出錯。

而後看一個稍微複雜的例子。 const int* p; 仍是先去掉const 修飾符號,獲得:int *p。

注意,下面兩個是等價的。 int* p; int *p;

其實咱們想要說的是,對於int *p:*p是int類型變量。那麼顯然,p就是指向int類型變量的指針,即p所指向的內存塊內所存儲的數據爲*p。

同理 const int* p; 其實等價於 const int (*p); int const (*p); 即,*p是常量。也就是說,p指向的數據是常量,也就是說:*p不可變,而p可變。

因而 p+=8; //合法

       *p=3; //非法,p指向的數據是常量。

那麼如何聲明一個自身是常量的指針呢?

方法是讓const儘量的靠近p;即:int* const p; const右面只有p,顯然,它修飾的是p,說明p不可被更改,也就是說:*p可變,而p不可變。

首先把const去掉,能夠看出int *p:p是一個指向 int類型變量的指針。 因而 p+=8; //非法,p不可變

                                                                                                  *p=3; //合法

再看一個更復雜的例子。

它是上面兩者的綜合 const int* const p; 說明p本身是常量,且p指向的int類型變量也是常量。也就是說:*p不可變,而p也不可變。

因而 p+=8; //非法,p不可變

      *p=3; //非法,*p不可變

const 還有一個做用就是用於修飾常量靜態字符串。

例如: const char* name=David;

若是沒有const,咱們可能會在後面有意無心的寫name[4]='x'這樣的語句,這樣會致使對只讀內存區域的賦值,而後程序會馬上異常終止。

有了 const,這個錯誤就 能在程序被編譯的時候就當即檢查出來。

這就是const的好處:讓邏輯錯誤在編譯期被發現。

const 還能夠用來修飾數組 const char s[]=David; 與上面有相似的做用。

二、在函數聲明時修飾參數 來看實際中的一個例子。

void * memmove(void *dst, const void *src, size_t len); // 這是標準庫中的一個函數,用於按字節方式複製字符串(內存)。

它的第一個參數:是將字符串複製到哪裏去(destination),即目的地,這段內存區域必須是可寫的,所以這個參數不能用const修飾。

它的第二個參數:是要將什麼樣的字符串複製出去,咱們對這段內存區域只作讀取,不寫,所以這個參數用const修飾。

因而,咱們站在這個函數本身的角度來看:

src 這個指針,它所指向的內存內所存儲的數據(*src)在整個函數執行的過程當中是不變的。因而src所指向的內容(*src)是常量。因而*src就須要用const修飾。

例如,咱們這裏這樣使用它:

const char* s=hello;

char buf[100];

memmove(buf,s,6); //這裏其實應該用strcpy或memcpy更好

若是咱們反過來寫: memmove(s,buf,6); 那麼編譯器必定會報錯。

這個報錯的事實是:咱們常常會把各類函數的參數的順序寫反。

其實是編譯器在此時幫了咱們大忙。若是編譯器靜悄悄的不報錯,(在函數聲明處去掉 const便可),那麼這個程序在運行的時候必定會崩潰。

這裏還要說明的一點是:在函數的參數聲明中,const通常用來聲明指針而不是基本類型變量。

例如,上面的memmove()函數中的size_t len這個參數,在函數實現的時候能夠徹底不用更改len的值,那麼是否應該把len也聲明爲常量呢?

能夠這麼作。咱們來分析這麼作有什麼優劣。

若是len加了const,那麼對於這個函數的調用者,能夠防止他在實現這個函數的時候修改不須要修改的值(len),這樣很好。

可是: 1 這個修飾符號毫無心義,咱們仍然能夠傳遞一個常量整數或者一個很是量整數過去,反正對方得到的只是咱們傳遞的一個copy。

          2 暴露了實現,我不須要知道你在實現這個函數的時候是否修改過len的值。

因此綜合1和2,const通常只用來修飾指針。

再看一個複雜的例子。

int execv(const char *path, char *const argv[]);

着重看第二個參數:char *const argv[]。

若是去掉const,咱們能夠看出 char * argv[]; argv是一個數組,它的每一個元素都是char *類型的指針。

若是加上const.那麼const修飾的是誰呢?

它修飾的是一個數組argv[]:意思就是說這個數組中的元素是隻讀的。

那麼這個數組中的元素的是什麼類型呢?是char *類型的指針,也就是說指針是常量,而它指向的數據不是。 因而 argv[1]=NULL; //非法

                                                                                                                                                argv[0][0]='a'; //合法

三、全局變量。

咱們的原則依然是:儘量少使用全局變量。

咱們的第二條規則是:儘量多使用const。

若是一個全局變量只在本文件中使用,那麼用法和前面所說的函數局部靜態變量沒有什麼區別。

/* MyFile.c */

static const double pi=3.14;

而若是它要在多個文件間共享,那麼就牽扯到一個存儲類型的問題。有兩種方式:

1.使用extern修飾

 /* file1.h */

extern const double pi;

/* file1.c */

const double pi=3.14;

而後在其餘須要使用pi這個變量的文件中:#include file1.h 或者,本身把那句外部變量聲明覆制一遍就好。

這樣作的結果是:整個程序連接完後,全部須要使用pi這個變量的文件共享同一個pi存儲區域。

2.使用static靜態外部存儲類

/* constant.h */

static const pi=3.14;

在須要使用這個變量的*.c文件中,必須包含這個頭文件。 前面的static必定不能少。不然連接的時候會報告說該變量被屢次定義。

這樣作的結果是:每一個包含了constant.h的*.c文件,都有一份該變量本身的copy, 該變量實際上仍是被定義了屢次,佔用了多個存儲空間,不過在加了static關鍵字 後,解決了文件間重定義的衝突。

壞處是:浪費了存儲空間,致使連接完後的可執行文件變大。

可是一般,這個小小几個字節的變化,不是問題。

好處是:你不用關心這個變量是在哪一個文件中被初始化的。

最後,說說const的做用。

const 的好處:引入了常量的概念,讓咱們不要去修改不應修改的內存。

直接的做用就是:讓更多的邏輯錯誤在編譯期被發現。因此咱們要儘量的多使用const。

可是不少人並不習慣使用它,更有甚者,是在整個程序 編寫/調試 完後才補 const。若是是給函數的聲明補const,尚好。

若是是給 全局/局部變量補const,那 麼……那麼,爲時已晚,無非是讓代碼看起來更漂亮了。

關於const的使用,曾有一 個笑話說,const 就像安全套,事前要記牢。若是作完後纔想起來該用而忘了用, 呵呵……呵呵……

相關文章
相關標籤/搜索