const
修飾符用於固定變量使其成為常數:
const float pi = 3.14;
pi = 3.14159; /*此行報錯,無法更改常數*/
但其實還是有方法可以修改
const float pi = 3.14;
float *p = π
*p = 3.14159;
/*這樣是可以對pi修改成功的*/
但會跳出warning,原因是const
對目標變數的特性已經被丟棄了
以設計理念來說已經決定將pi
常數化
但後續的操作又改變其值
因此這個warning廣意上來說接近error了……(編譯成功,但性質惡劣)
const亦可以拿來修飾指標
然而根據擺放的位置會有兩種:
const int * p
以及int * const p
兩種宣告方法產生的效果與其意義完全不同
const int * p
:對常數的指標,地址上的值不變
int * const p
:指標常數化,地址不變
const int * p
:int i = 1;
const int * p = &i;
printf(" %d " , *p);
/*輸出結果 :1 */
這時若直接嘗試修改指標指向的值
則會報錯:
*p = 10;
/*這行是錯的 , 嘗試對*p賦值報錯,原因是該地址上的值被常數化*/
然而可以直接更改變數i
達到更改效果:
int i = 1;
const int * p = &i;
i = 10;/*i是變數,可以更改*/
printf(" %d " , *p);
/*輸出結果 :10*/
另外const int * p
常量化的對象是透過p
訪問的值
因此p
本身也可以更改指向的記憶體區塊
以剛剛的程式碼為例:
int i = 1;
const int * p = &i;
int j = 100;
p = &j ; /*將指標指向j的位址*/
printf(" %d " , *p);
/*輸出結果 : 100*/
此操作合法,成功將p
指向變數j
的記憶體位址
所以再回到這篇筆記最一開始的圓周率pi
範例
若在第二行中使用const
修飾 *p
就可以完全確保不會有前後矛盾的修改行為:
const float p = 3.14;
const float *p = π
*p = 3.14159; /*不成立,p*/
int * const p
可以改變指向位址中的值
int i = 1 ;
int * const p = &i;
printf("before :%d\\n " , *p);
*p = 10 ;
printf("after :%d\\n" , *p);
/*
輸出:
before :1
after :10
*/
但無法更改指標的指向:
int i = 1 ;
int * const p = &i;
int j = 5;
p = &j; /*此行報錯,無法改變p指向的位址*/
const int * const p
:先宣告一變數讓p
指向它
int i = 1;
const int * const p = &i;
printf("%d \\n" , *p);
/*輸出結果:1*/
望文生義,可以想像這樣的宣告方法
是位址與指標訪問的值都常數化
因此對於改變p的指向或者改變p訪問地址上的值
都是不合法的
int i = 1;
const int * const p = &i;
int j = 100;
p = &j ; /*不合法,指針指向位址已常數化*/
*p = 100 ; /*也不合法,透過p指針訪問的值已常數化*/
如果去看一些底層函數的宣告
例如POSIX標準中的open
:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
可以看到const char *pathname
也是使用const
進行修飾
使用 const
可以防止意外地修改指標所指向的字符內容
在 open
函數中pathname
代表文件的路徑名
這個路徑名不應該被修改
因此使用 const
可以保證這個字串在函數內部不會被改變
還有像是字串操作常用的strcpy
原始的函數宣告如下:
char *strcpy(char *dest, const char *src);
對於src
也是使用const
防止意外地修改指標所指向內容