當前位置:才華齋>計算機>C語言>

C語言關鍵字const用法

C語言 閱讀(8.85K)

導語:C是高階語言,它把高階語言的基本結構和語句與低階語言的實用性結合起來。下面就由小編為大家介紹一下C語言關鍵字const用法,歡迎大家閱讀!

C語言關鍵字const用法

  c語言中的const">C語言中的const

const是C語言中保留的一個關鍵字,用來定義常量,如果一個變數被const修飾,那麼它的值就不能被改變。使用符號常量寫出的程式碼更容易維護;一些指標常常是邊讀邊移動,而不是邊寫邊移動;許多程式設計規範對於函式引數會強制要求只讀不寫,在這些情況下,都需要藉助const實現。

那麼有些人會問,C語言中不是已經存在#define了嗎,為什麼還要使用const呢?相比於#define,const修飾符有如下優勢:

1. const能夠使編譯器進行型別檢查,而預編譯指令#define只是簡單的對值進行替換。

2. const可以保護被修飾的變數等,防止變數因為意外被修改,從而增強程式的健壯性。

3. 編譯器通常不為普通const常量分配儲存空間,而是將他們儲存在符號表中,這使得它成為了一個編譯期間的常量,沒有了儲存於記憶體操作,使得它的效率很高。

  const用法

const最常見的用法就是作為陣列的邊界和switch分情況標號,分類如下:

常變數: const + 型別說明符 + 變數名

常引用: const + 型別說明符 + &引用名

常物件: 類名 + const 物件名

常成員函式: 類名::fun(形參) + const

常陣列: 型別說明符 + const 陣列名[大小]

常量指標: const + 型別說明符* 指標名 或者 型別說明符 + const *指標名

首先提示的是:在常變數(const + 型別說明符 + 變數名)、常引用(const + 型別說明符 + &引用名)、常物件(類名 + const 物件名)、 常陣列(型別說明符 + const 陣列名[大小]), const”與“型別說明符”或“類名”(其實類名是一種自定義的型別說明符) 的位置可以互換。如:

12const int a=5; int const a=5; //等同

其實這種可以很好理解,const只能修飾int(型別名或類名),所以二者是等同的。

 修飾區域性變數

12const int n=5; int const n=5;

這兩種寫法是一樣的,都是表示變數n的值不能被改變了,需要注意的是,用const修飾變數時,一定要給變數初始化(用來修飾函式的形參除外),否則之後就不能再進行賦值了。C語言中,const定義的常量是全域性的,C++中則是視宣告位置而定。

接下來看看const用於修飾常量靜態字串,例如:

1const char* str = "fdsafdsa";

如果沒有const的修飾,我們可能會在後面有意無意的寫str[4]=’x’這樣的語句,這樣會導致對只讀記憶體區域的賦值,然後程式會立刻異常終止。有了const,這個錯誤就能在程式被編譯的時候就立即檢查出來,這就是const的好處。讓邏輯錯誤在編譯期被發現。

 常量指標與指標常量

常量指標

常量指標表示一個指向常量的指標,即該指標指向的內容是個常量(至少該常量指標是這樣認為的),可以有如下的定義方式:

12const int * n; int const * n;

 需要注意以下兩點:

1.常量指標指向的值是不能改變的,但是這不意味指標本身不能改變,常量指標是可以指向其他的地址的。

12345int a=5; int b=6; const int* n = &a; int const *n1 = &a; n=&b;

這個例子中,n和n1是等價的,都是指向a的一個常量指標,這時候有人會說,常量指標指向的應該是一個常量吧,但是a並不是一個常量啊,為什麼常量指標依舊可以指向a呢,這時候又要注意一下了:

編譯器允許把非 const 物件的地址賦給指向 const 物件的指標,不允許把一個 const 物件的地址賦給一個普通的、非 const 物件的指標。

其實這個也很好理解,對於一個變數,我可以對你增加束縛,用指向const物件的指標指向你,這只是意味著我現在認為你是常量,不能通過這個常量指標對你進行修改,但是對於第二種情況就不一樣了,你本來是一個被束縛的常量,如果用一個普通的指標指向你的話,就意味著我認為你這個常量是一個變數,這樣或許我會在下面的程式碼中,對你的值進行修改,對一個const常量進行修改,會發生什麼,你應該明白。

然後回到上面的`程式碼,我們將a的地址給了n,然後在下一行又改變了n的值,將b的地址賦給了n,這是沒有任何問題的,因為n只是一個指標,指向了一個常量,所以被稱為常量指標,它本身依舊是一個普通的指標,比較特殊的是它所指向的內容,而不是它本身。所以它本身的值可以修改,指向不同的內容。

2.當常量指標指向了一個變數時,不能通過這個常量指標改變變數的值,但是還是可以通過其他的引用來改變變數的值的。

123456int a=5; const int* n=&a; a=6; // 正確 *n = 7; //錯誤,對於常量指標,不能通過該指標來改變所指向的內容(即使它所指向的內容並不是一個常量)。 int *ni = (int *)n; *n = 100; //正確

在n這個指標眼中,n所指向的內容是一個常量,所以不允許別人通過n本身對a進行修改,但是a本質上是一個變數,所以我們可以直接通過變數名或者新的普通指標對a進行修改。實際上,在將程式載入記憶體的時候,會有專門的一塊記憶體區域來存放常量。但是,上面的a本身不是常量,是存放在棧或者堆中的。我們仍然可以修改它的值。而n不能修改指向的值應該說是編譯器的一個限制。

  指標常量

指標常量是指指標本身是一個常量,它只能指向一個固定的地址,不能指向別的地址。寫法如下:

1int *const n;

和上面的常量指標不一樣,指標常量是指指標本身比較特殊,而它所指的內容並不特殊,因此,指標常量指向的內容是可以修改的,可以通過別的指向該地址的指標進行修改。如下所示:

1234int a=5; int *p=&a; int* const n=&a; *p=8;

在函式中,指標常量時表示不允許將該指標指向其他內容:

1234567891011121314void func_02(int* const p) { int *pi = new int(100); /* 錯誤!P是指標常量。不能對它賦值。 */ p = pi; } int main() { int* p = new int(10); func_02(p); p; return 0; }

然後看下面的程式碼:

12const int *m1 = new int(10); int* const m2 = new int(20);

在上面的兩個表示式中,最容易讓人迷惑的是const到底是修飾指標還是指標指向的記憶體區域?其實,只要知道:const只對它左邊的東西起作用,唯一的例外就是const本身就是最左邊的修飾符,那麼它才會對右邊的東西起作用。根據這個規則來判斷,m1應該是常量指標(即不能通過m1來修改它所指向的內容。);而m2應該是指標常量(即不能讓m2指向其他的記憶體地址)。或者我們可以把星號看做指標,把const看做常量,那麼m1就是常量指標,m2就是指標常量。

  指向常量的常指標

是以上兩種的結合,指標指向的位置不能改變並且也不能通過這個指標改變變數的值,但是當它指向一個變數時,依然可以通過其他的普通指標改變變數的值。

const int* const p;

  修飾函式的引數

根據常量指標與指標常量,const修飾函式的引數也是分為三種情況

1.防止修改指標指向的內容

1void StringCopy(char *strDestination, const char *strSource);

其中 strSource 是輸入引數,strDestination 是輸出引數。給 strSource 加上 const 修飾後,如果函式體內的語句試圖改動 strSource 的內容,編譯器將指出錯誤。這種形式通常用於在陣列形式的引數中模擬傳值呼叫。也就是相當於函式呼叫者聲稱:”我給你一個指向它的指標,但你不能去修改它。”如果函式編寫者遵循了這個約定,那麼就相當於模擬了值傳遞。這也是const最有用之處了:用來限定函式的形參,這樣該函式將不會修改實參指標所指的資料。這裡注意了,是函式不應該去修改而不是不能修改,也就是說const不能阻止引數的修改(原因見上)。

2.防止修改指標指向的地址

1void swap (int* const p1 , int* const p2);

指標p1和指標p2都是指標常量,指向的地址都不能修改。

  修飾函式的返回值

很多時候,我們的函式中會返回一個地址或者引用。呼叫這得到這個返回的地址或者引用後就可以修改所指向或者代表的物件。這個時候如果我們不希望這個函式的呼叫這修改這個返回的內容,就應該返回一個常量。

如果給以“指標傳遞”方式的函式返回值加 const 修飾,那麼函式返回值(即指標)的內容不能被修改,該返回值只能被賦給加const 修飾的同類型指標。

例如函式

1const char * GetString(void);

如下語句將出現編譯錯誤:

1char *str = GetString();

正確的用法是

1const char *str = GetString();

  修飾全域性變數

全域性變數的作用域是整個檔案,我們應該儘量避免使用全域性變數,以為一旦有一個函式改變了全域性變數的值,它也會影響到其他引用這個變數的函式,導致除了bug後很難發現,如果一定要用全域性變數,我們應該儘量的使用const修飾符進行修飾,這樣方式不必要的以為修改,使用的方法與區域性變數是相同的。

const 在c和c++中的區別

C++中的const正常情況下是看成編譯期的常量,編譯器並不為const分配空間,只是在編譯的時候將期值儲存在名字表中,並在適當的時候摺合在程式碼中.所以,以下程式碼:

using namespace std;

int main()

{

const int a = 1;

const int b = 2;

int array[ a + b ] = {0};

for (int i = 0; i < sizeof array / sizeof *array; i++)

{

cout << array << endl;

}

}

在可以通過編譯,並且正常執行.但稍加修改後,放在C編譯器中,便會出現錯誤:

int main()

{

int i;

const int a = 1;

const int b = 2;

int array[ a + b ] = {0};

for (i = 0; i < sizeof array / sizeof *array; i++)

{

printf(“%d”,array);

}

}

  錯誤訊息:

c: est1 e.c(8): error C2057: 應輸入常數表示式

c: est1 e.c(8): error C2466: 不能分配常數大小為 0 的陣列

出現這種情況的原因是:在C中,const是一個不能被改變的普通變數,既然是變數,就要佔用儲存空間,所以編譯器不知道編譯時的值.而且,陣列定義時的下標必須為常量. 在C語言中: const int size; 這個語句是正確的,因為它被C編譯器看作一個宣告,指明在別的地方分配儲存空間.但在C++中這樣寫是不正確的.C++中const預設是內部連線,如果想在C++中達到以上的效果,必須要用extern關鍵字.即C++中,const預設使用內部連線.而C中使用外部連線.

(1) 內連線:編譯器只對正被編譯的檔案建立儲存空間,別的檔案可以使用相同的表示符或全域性變數.C/C++中內連線使用static關鍵字指定.

(2) 外連線:所有被編譯過的檔案建立一片單獨儲存空間.一旦空間被建立,聯結器必須解決對這片儲存空間的引用.全域性變數和函式使用外部連線.通過extern關鍵字宣告,可以從其他檔案訪問相應的變數和函式.