const用法總結

轉自:http://yolinsky.blog.sohu.com/57710493.html


const在C語言中算是一個比較新的描述符,我們稱之爲常量修飾符,意即其所修飾的對象爲常量(immutable)。


我們來分情況看語法上它該如何被使用。

1、函數體內修飾局部變量。
例:
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";
與上面有類似的作用。

2、在函數聲明時修飾參數
來看實際中的一個例子。
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'; //合法


3、全局變量。
我們的原則依然是,儘可能少的使用全局變量。
我們的第二條規則 則是,儘可能多的使用const。
如果一個全局變量只在本文件中使用,那麼用法和前面所說的函數局部變量沒有什麼區別。
如果它要在多個文件間共享,那麼就牽扯到一個存儲類型的問題。

有兩種方式。
1.使用extern
例如
/* file1.h */
extern const double pi;
/* file1.c */
const double pi=3.14;
然後其他需要使用pi這個變量的,包含file1.h
#include "file1.h"
或者,自己把那句聲明覆制一遍就好。
這樣做的結果是,整個程序鏈接完後,所有需要使用pi這個變量的共享一個存儲區域。

2.使用static,靜態外部存儲類
/* constant.h */
static const pi=3.14;
需要使用這個變量的*.c文件中,必須包含這個頭文件。
前面的static一定不能少。否則鏈接的時候會報告說該變量被多次定義。
這樣做的結果是,每個包含了constant.h的*.c文件,都有一份該變量自己的copy,該變量實際上還是被定義了多次,佔用了多個存儲空間,不過在加了static關鍵字後,解決了文件間重定義的衝突。壞處是浪費了存儲空間,導致鏈接完後的可執行文件變大。但是通常,這個,小小几字節的變化,不是問題。好處是,你不用關心這個變量是在哪個文件中被初始化的。


最後,說說const的作用。
const 的好處,是引入了常量的概念,讓我們不要去修改不該修改的內存。直接的作用就是讓更多的邏輯錯誤在編譯期被發現。所以我們要儘可能的多使用const。但是很多人並不習慣使用它,更有甚者,是在整個程序 編寫/調試 完後才補const。如果是給函數的聲明補const,尚好。如果是給 全局/局部變量補const,那
麼……那麼,爲時已晚,無非是讓代碼看起來更漂亮了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章