程式扎記: [ C 文章收集 ] C 的關鍵字 — const的理解和用法

標籤

2013年4月18日 星期四

[ C 文章收集 ] C 的關鍵字 — const的理解和用法

來源自 這裡 
Preface: 
const在C中的用法很靈活(相信C++中也一樣),個人感覺對之既愛又恨,有時候感覺const很好用,同時又經常會因為它的優點而犯錯,犯錯的原因除了粗心之外,另一個更重要的,就是以前對const理解不到位。於是今天自己寫成一篇小總結. 

關於const的具體定義: 
個人感覺很難對它下一個標準的定義,因為的用法很靈活,似乎對它定義後總無法讓人能夠明白它的意思,而且容易讓人產生誤解(也許是偶水準太菜了)。例如,把它有定義:一個能夠讓變數變成無法修改的常量的關鍵字。那麼,這樣的話,就可能讓人誤解為只要有const在定義變數裡面,那變數就無論怎樣都無法修改。這樣的理解是很片面的(下面用法方面將對這問題做探討)。因此,本人在此不敢對它下定義,其他參考書好象也沒有下定義. 

關於const的具體作用: 
const 作用就靈活了,一個運算式中const放置的位置不同,效果可能就不一樣了。下面分具體情況分析(當然,所舉的情況並非覆蓋全部情況). 

const 最經常的用法 
為了防止傳遞的函數參數不被修改,在調用函數的形參中用 const 關鍵字, 底下為一個簡單範例: 
  1. int FindNum(const int array[], int num, int conut);//聲明函數  
  2. //code...  
  3.   
  4. int FindNum(const int array[], int num, int count)  
  5. {  
  6.     int i;  
  7.     int flag = 1;  
  8.   
  9.     for (i = 0; (i < count) && flag; i++)  
  10.     {  
  11.         if (array[i] == num)  
  12.        {  
  13.            flag = 0;  
  14.            break;  
  15.     }  
  16.     return flag;  
  17. }  
  18. //code...  
上面例子,編譯器會把array[]當作常量資料的陣列看待。所以,假如你不小心給陣列賦值,那麼,編譯器就會報錯了。因此,當你不需要也不想修改陣列的資料時,最好用const把陣列定義為常量陣列. 

const可以用來創建陣列常量、指標常量、指向常量的指標等 
const char ch = 'a';
const int a[5] = {1, 2, 3, 4, 5};
const int *p = a; //a是一個陣列的首位址.p是指向常量的指標
int * const p = a; //a是一個陣列的首位址.p是指針常量;
const int * const p = a; //a是一個陣列的首位址。p是指向常量的指標常量
前兩種情況很簡單,現在著重分析一下後三種用法,因為這3種情況容易出錯. 首先來看: 
  1. const int *p = a;  
p 是指向常量的指標,因此,不可以通過給指標賦值來改變陣列中的資料,例如: 
  1. *p = 10;       /*錯誤*/  
  2. *(p + 2) = 1;  /*錯誤*/  
假如指向常量指標可以改變值,那麼,就等於也改變了陣列的數. 接著來看: 
  1. int * const p = a;  
看這運算式,const 的位置和第一個不同吧!他們的用法和作用就完全不一樣了。這時候 p 是指標常量,我們知道,指標是指向了一個陣列的首位址,那麼,它的位置就不可以改變了。但是你現在應該和第一個運算式比較了,現在的陣列並不是常量陣列,所以陣列的資料是可以改變的,而指標這時候它是不可以移動的,指向陣列第一個資料,所以它可以而且只可以改變陣列第一個 數據的值。這一點請別誤解,指標常量只是它的位址不可以改變,並不是它指向的內容一定不可以改變,這一點切記! 

下面的代碼是指標常量的使用範例: 
  1. *p = 2;          /*可以*/  
  2. *(p+1) = 10;     /*可以*/  
  3. p++;             /*不可以*/  
接著來看: 
  1. const int * const p = a;  
假如前面兩種運算式的本質你理解了,這種運算式你來理解根本沒有問題,const現在有兩個,而且一個const的位置是第一種情況的位置,第二個const是第二種情況的位置,所以這運算式的功能就是前兩種情況的作用總合: 
  1. *p = 2;             /*不可以*/   
  2. *(p + 2) = 10;   /*不可以*/  
  3. p++;                /*不可以*/  
const並不會阻止參數的修改 
之所以把這作為一點來談,就是因為直覺會以為在函數參數中用了const就一定不可以改變參數,這實際上是錯誤的理解,因為,它並不阻止參數的修改,下面舉個簡單的例子來闡述一下: 
  1. #include  
  2. #include  
  3. void ChangeStr(const char *String);  
  4. int main(void)  
  5. {  
  6.     char str[] = "The C programme";  
  7.   
  8.     ChangeStr(str);  
  9.     printf(str);  
  10.   
  11.     system("Pause");  
  12.     return 0;  
  13. }  
  14.   
  15. void ChangeStr(const char *String)  
  16. {  
  17.     char *Source = (char *)String;  
  18.   
  19.     while (*Source)  
  20.     {  
  21.         *Source = toupper(*Source);  
  22.         Source++;  
  23.     }  
  24. }  
  25.   
  26. //end  
上面的程式把字串中的每個字元都轉換成大寫字母了。因為*String把地址給了*Source,而*Source的值的改變編譯器並不干涉,可能有的編譯器會發出警告之類。上面的程式只是為了說明const並不會阻止參數的修改,如果象上面程式那樣,個人感覺沒什麼意義,只會讓人容易混亂而已! 

Supplement: 
CProgramming.com > Const Correctness

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!