1. 程式人生 > 實用技巧 ># const關鍵字

# const關鍵字

1、遇到的情景

int MaxSubsequenceSum(const int A[], int N)
{
    int ThisSum, MaxSum, j;
    
    ThisSum = MaxSum = 0;
    for(j = 0; j < N; j++)
    {
        ThisSum += A[j];
        
        if(ThisSum > MaxSum)
            MaxSum = ThisSum;
        else if(ThisSum < 0)
            ThisSum = 0;
    }
    return MaxSum;
}

const限定符在這裡的作用是使得傳入的陣列得狀態是隻讀,就是無法修改。

2、擴充

C90標準中新增了const關鍵字,用於限定一個變數為只讀(在C語言中,用const型別限定符宣告的是變數,不是常量)。其宣告如下:

const int MONTHS = 12; // MONTH在程式中不可更改,值為12

這使得MONTHS成為一個只讀值。也就是說,可以在計算機中使用MONTHS,可以列印MONTHS,但是不能更改MONTHS的值。

注意,const的用法比#define指令更加靈活。可以建立const陣列、const指標和指向const的指標。

  • 對形參使用const

ANSI C提供了一種預防手段。如果函式的意圖不是修改陣列中的資料內容,那麼在函式原型和函式定義中宣告形式引數時應使用關鍵字const。例如,sum()函式的原型和定義如下:

int sum(const int ar[], int n); /* 函式原型 */
int sum(const int ar[], int n) /* 函式定義 */
{
    int i;
    int total = 0;
    for( i = 0; i < n; i++)
    total += ar[i];
    return total;
}

以上程式碼中的const告訴編譯器,該函式不能修改ar指向的陣列中的內容。如果在函式中不小心使用類似ar[i]++的表示式,編譯器會捕獲這個錯誤,並生成一條錯誤資訊。這裡一定要理解,這樣使用const並不是要求原陣列是常量,而是該函式在處理陣列時將其視為常量,不可更改。這樣使用const可以保護陣列的資料不被修改,就像按值傳遞可以保護基本資料型別的原始值不被改變一樣。一般而言,如果編寫的函式需要修改陣列,在宣告陣列形參時則不使用const;如果編寫的函式不用修改陣列,那麼在宣告陣列形參時最好使用const。

  • 使用const關鍵字保護陣列
#define MONTHS 12
...
const int days[MONTHS] = {31,28,31,30,31,**,**,**,**,31,30,31};

如果程式稍後嘗試改變陣列元素的值,編譯器將生成一個編譯期錯誤訊息:

days[9] = 44; /* 編譯錯誤 */
  • 指向const的指標不能用於改變值。考慮下面的程式碼:
double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};
const double * pd = rates;        // pd指向陣列的首元素

第2行程式碼把pd指向的double型別的值宣告為const,這表明不能使用pd來更改它所指向的值:

*pd = 29.89;        // 不允許
pd[2] = 222.22;     // 不允許
rates[0] = 99.99;   // 允許,因為rates未被const限定

無論是使用指標表示法還是陣列表示法,都不允許使用pd修改它所指向資料的值。但是要注意,因為rates並未被宣告為const,所以仍然可以通過rates修改元素的值。另外,可以讓pd指向別處:

pd++; /* 讓pd指向rates[1] -- 沒問題 */
  • 指向const的指標通常用於函式形參中,表明該函式不會使用指標改變資料。例如:
void show_array(const double *ar, int n);
  • 關於指標賦值和const需要注意一些規則。

首先,把const資料或非const資料的地址初始化為指向const的指標或為其賦值是合法的:

double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};
const double locked[4] = {0.08, 0.075, 0.0725, 0.07};
const double * pc = rates;    // 有效
pc = locked;                  // 有效
pc = &rates[3];               // 有效

然而,只能把非const資料的地址賦給普通指標:

double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};
const double locked[4] = {0.08, 0.075, 0.0725, 0.07};
double * pnc = rates;    // 有效
pnc = locked;            // 無效
pnc = &rates[3];         // 有效

這個規則非常合理。否則,通過指標就能改變const陣列中的資料。

應用以上規則的例子,如show_array()函式可以接受普通陣列名和const陣列名作為引數,因為這兩種引數都可以用來初始化指向const的指標:

show_array(rates, 5);        // 有效
show_array(locked, 4);       // 有效

這樣一來,就不可以通過指標改變陣列中的資料了。因此,對函式的形參使用const不僅能保護資料,還能讓函式處理const陣列。

另外,C標準規定,使用非const識別符號修改const資料導致的結果是未定義的。即在函式的形參使用了const限定符,而傳入的實參沒有使用const限定符,這樣的用法是不建議的。

  • const還有其他的用法。例如,可以宣告並初始化一個不能指向別處的指標,關鍵是const的位置:
double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};
double * const pc = rates;  // pc指向陣列的開始
pc = &rates[2];             // 不允許,因為該指標不能指向別處
*pc = 92.99;                // 沒問題 -- 更改rates[0]的值
  • 最後,在建立指標時還可以使用const兩次,該指標既不能更改它所指向的地址,也不能修改指向地址上的值:
double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};
const double * const pc = rates;
pc = &rates[2];     //不允許
*pc = 92.99;        //不允許

說起來,這些個用法確實是有些繁瑣,還是需要在實際使用的過程中來慢慢鞏固的。

參考與摘錄:《C Primer Plus》


未完待續...