一、簡介
(1)const關鍵字,在C語言中用來修飾變量,表示這個變量不可更改。
我們可能首先想到的是經過它修飾的變量便是常量了。其實我們這種想法是錯誤的,其實 const 修飾的變量是隻讀的,其本質還是變量。它只是告訴編譯器該變量不能出現在賦值符號的左邊。
那麼,const修飾的變量真的不能修改嗎?我麼看如下代碼
#include <stdio.h>
int main(void)
{
const int a = 5;
//a = 6; // error: assignment of read-only variable ‘a’
int *p;
p = (int *)&a; // 這裏報警高可以通過強制類型轉換來消除
*p = 6;
printf("a = %d.\n", a); // a = 6,結果證明const類型的變量被改了
return 0;
}
- const修飾的變量其實是可以改的(前提是gcc環境下)。
- 對於某些編譯器,const修飾的變量是不可以改的。const修飾的變量到底能不能真的被修改,取決於具體的環境,C語言本身並沒有完全嚴格一致的要求。
- 在gcc中,const是通過編譯器在編譯的時候執行檢查來確保實現的(也就是說const類型的變量不能改是編譯錯誤,不是運行時錯誤。)所以我們只要想辦法騙過編譯器,就可以修改const定義的常量,而運行時不會報錯。
對於const與編譯器的關係詳見如下文章:https://blog.51cto.com/12810168/2095264
二、const關鍵字與指針
const修飾指針有4種形式,區分清楚這4種即可全部理解const和指針。
第一種:const int *p;
第二種:int const *p;
第三種:int * const p;
第四種:const int * const p;
關於指針變量的理解,主要涉及到2個變量:第一個是指針變量p本身,第二個是p指向的那個變量(*p)。一個const關鍵字只能修飾一個變量,所以弄清楚這4個表達式的關鍵就是搞清楚const放在某個位置是修飾誰的。
const後面要是直接跟着指針變量,就是修飾指針變量。例如 int * const p3; // p本身是cosnt的,p指向的變量不是const的
- 常量指針是指針指向的內容是常量;
- 指針常量是指指針本身是個常量,不能在指向其他的地址;
- 指向常量的常指針,以上兩種的結合。
#include <stdio.h>
int main(void)
{
int a = 5;
// 第一種
const int *p1; // p本身不是cosnt的,而p指向的變量是const的
// 第二種
int const *p2; // p本身不是cosnt的,而p指向的變量是const的
// 第三種
int * const p3; // p本身是cosnt的,p指向的變量不是const的
// 第四種
const int * const p4;// p本身是cosnt的,p指向的變量也是const的
*p1 = 3; // error: assignment of read-only location ‘*p1’
p1 = &a; // 編譯無錯誤無警告
*p2 = 5; // error: assignment of read-only location ‘*p2’
p2 = &a; // 編譯無錯誤無警告
*p3 = 5; // 編譯無錯誤無警告
p3 = &a; // error: assignment of read-only variable ‘p3’
p4 = &a; // error: assignment of read-only variable ‘p4’
*p4 = 5; // error: assignment of read-only location ‘*p4’
return 0;
}
note:const與typedef結合使用時,需要注意:
(1)typedef int *PINT; const PINT p2; 相當於是int *const p2;
(2)typedef int *PINT; PINT const p2; 相當於是int *const p2;
(3)如果確實想得到const int *p;這種效果,只能typedef const int *CPINT; CPINT p1;
三、const關鍵字與函數形參
(1)const一般用在函數參數列表中,用法是const int *p;(意義是指針變量p本身可變的,而p所指向的變量是不可變的)。
(2)const用來修飾指針做函數傳參,作用就在於聲明在函數內部不會改變這個指針所指向的內容,所以給該函數傳一個不可改變的指針(char *p = "linux";這種)不會觸發錯誤;而一個未聲明爲const的指針的函數,你給他傳一個不可更改的指針的時候就要小心了。
(3)編程中函數的輸入和輸出都是靠函數形參的,返回值只是用來表示函數執行的結果是對(成功)還是錯(失敗)。如果這個參數是用來做輸入的,就叫輸入型參數;如果這個參數的目的是用來做輸出的,就叫輸出型參數。(在典型的linux風格函數中,返回值是不用來返回結果的,而是用來返回0或者負數用來表示程序執行結果是對還是錯,是成功還是失敗)。
#include <stdio.h>
int multip5_3(int a, int *p);
int main(void)
{
int a, b = 0, ret = -1;
a = 30;
ret = multip5_3(a, &b);
if (ret == -1)
{
printf("出錯了\n");
}
else
{
printf("result = %d.\n", b);
}
return 0;
}
int multip5_3(int a, int *p)
{
int tmp;
tmp = 5 * a;
if (tmp > 100)
{
return -1;
}
else
{
*p = tmp;
return 0;
}
}
小結:
看到一個函數的原型後,怎麼樣一眼看出來哪個參數做輸入哪個做輸出?函數傳參如果傳的是普通變量(不是指針)那肯定是輸入型參數;如果傳指針就有2種可能性了,爲了區別,經常的做法是:如果這個參數是做輸入的(通常做輸入的在函數內部只需要讀取這個參數而不會需要更改它)就在指針前面加const來修飾;如果函數形參是指針變量並且還沒加const,那麼就表示這個參數是用來做輸出型參數的。
譬如C庫函數(#include <string.h> 和 #include <stdio.h>)中strcpy函數
char *strcpy(char* dest, const char *src);
REF: