const
限定符和指針結合起來常見的情況有以下幾種。
const int *a; int const *a;
這兩種寫法是一樣的,a
是一個指向const int
型的指針,a
所指向的內存單元不可改寫,所以(*a)++
是不允許的,但a
可以改寫,所以a++
是允許的。
int * const a;
a
是一個指向int
型的const
指針,*a
是可以改寫的,但a
不允許改寫。
int const * const a;
a
是一個指向const int
型的const
指針,因此*a
和a
都不允許改寫。
指向非const
變數的指針或者非const
變數的地址可以傳給指向const
變數的指針,編譯器可以做隱式類型轉換,例如:
char c = 'a'; const char *pc = &c;
但是,指向const
變數的指針或者const
變數的地址不可以傳給指向非const
變數的指針,以免透過後者意外改寫了前者所指向的內存單元,例如對下面的代碼編譯器會報警告:
const char c = 'a'; char *pc = &c;
即使不用const
限定符也能寫出功能正確的程序,但良好的編程習慣應該儘可能多地使用const
,因為:
const
給讀代碼的人傳達非常有用的信息。比如一個函數的參數是const char *
,你在調用這個函數時就可以放心地傳給它char *
或const char *
指針,而不必擔心指針所指的內存單元被改寫。
儘可能多地使用const
限定符,把不該變的都聲明成只讀,這樣可以依靠編譯器檢查程序中的Bug,防止意外改寫數據。
const
對編譯器優化是一個有用的提示,編譯器也許會把const
變數優化成常量。
在第 3 節 “變數的存儲佈局”我們看到,字元串字面值通常分配在.rodata
段,而在第 4 節 “字元串”提到,字元串字面值類似於數組名,做右值使用時自動轉換成指向首元素的指針,這種指針應該是const char *
型。我們知道printf
函數原型的第一個參數是const char *
型,可以把char *
或const char *
指針傳給它,所以下面這些調用都是合法的:
const char *p = "abcd"; const char str1[5] = "abcd"; char str2[5] = "abcd"; printf(p); printf(str1); printf(str2); printf("abcd");
注意上面第一行,如果要定義一個指針指向字元串字面值,這個指針應該是const char *
型,如果寫成char *p = "abcd";
就不好了,有隱患,例如:
int main(void) { char *p = "abcd"; ... *p = 'A'; ... }
p
指向.rodata
段,不允許改寫,但編譯器不會報錯,在運行時會出現段錯誤。