1999年2月《Embedded Systems Programming》上刊登的《const T vs. T const》,作者是Dan Saks。
觀點1:
任何一個申明都由兩主要部分組成:一個或者幾個限定符(declaration specifier)和一序列由逗號隔開的申明變量(declarator)。舉個例子:static unsigned long int *x[N];
其中:static unsigned long int 限定符部分;
*x[N] 申明變量部分;
申明變量部分是要申明的變量的名字,它可能和* , [] , () , &(for C++)結合起來使用。我們知道,*用于申明表示變量是指針類型;[]意味著數組;()有兩種用法,第一種是作為函數調用操作符;另外一種是用作分組符。
對于上面的例子,x是指向數組(數組元素類型是static unsigned long int)的指針,還是x是一個數組,數組的元素是static unsigned long int* 類型呢?為此,引入觀點2。
觀點2:
申明變量中如果有操作符(例如 * [] ),按照表達式運算中的優(yōu)先級規(guī)則進行處理。
我們知道,在表達式運算中,* 的優(yōu)先級比 [] 低。同樣在申明變量的處理也是如此。如此以來,上面的疑問就可以解決了。我們看declarator部分:*x[N];由于[]的優(yōu)先級比較高,所以x是 一個數組在x是一個指針之前。如此以來,*就用來修飾數組元素了。
對于(),如果用作函數調用,它得優(yōu)先級和[]一樣;如果是用于分組作用,它得優(yōu)先級最高。
觀點3:
對于變量申明限定符部分,可能有類型限定符,還可能有非類型限定符(例如:static , extern , virtual)。類型限定符只直接作用于申明體(申明的變量)的類型;而非類型限定符直接作用于申明體。
繼續(xù)拿前面的例子,x是一個數組,unsigned long int是類型限定符,表示x這個數組的元素類型;而static是非類型限定符,指示x是靜態(tài)分配內存。
觀點4:
對于觀點3,非類型限定符主要是針對static來說的,對于const 和volatile來說,它們是類型限定符。
舉個例子:const void *vectortable[N]
如果把const當作非類型限定符的話,按照觀點3來分析,vectortable是一個數組,const由于是非類型限定符,所以是修飾 vectortable的,于是vectortable是一個指向數組的常量指針,數組元素的類型是void *。事實上不是這樣,const是類型限定符,修飾變量vectortable的類型的,這樣vectortable是一個指向數組的指針,數組元素類型 是const void *。
觀點5:
限定部分的各個限定詞之間的前后順序沒關系。
例如:const VP t; 和 VP const T等價
const char *p 等價于 char const *p;
說明:大多數資料和程序員都習慣將static非類型限定符放在變量申明的最前面,實際上這僅僅是習慣的問題,并不是語言自身的規(guī)定。
觀點6:
一種申明風格:對于限定部分里面的各個類型限定詞,如果有const ,最好把const 放在右邊而不是左邊。盡量使用 T const 代替 const T,避免錯誤。
例如:const char *p; 我們這樣寫: char const *p。之所以這樣做,是為了可讀性。注意這個可讀性是針對人的,而不是針對編譯器的。前面觀點5說了,編譯器不區(qū)分這個順序。下面我們看看這樣書寫風格怎 樣達到可讀性好的效果。
T const *p : 從右往左讀(*作為分隔符,標記指針的):p是指針,指向const T;
等價于:const T *p;
T * const p : 從右往左,常量指針指向T
由上可見方便之處了。我們只要按照從右往左邊順序就可以讀出來申明的意義。
除此之外,這樣寫還不會出錯。我們看看下面的一個例子。
typedef int *IP;
int a = 3;
const IP t = &a;
此時t是啥類型呢?
按照以前風格,我們替換IP為 int * 得到:const int * t;如此一來,等價于int const * t;也就是說t是指向常整形指針。實際上是否是這樣的呢?
答案是否定的。實際上t是指向整形的常指針。正確的理解是代替IP用如下方式:
const IP t
int *const t;
因此,在申明變量的時候,將const放在限定部分的最右邊是一種比較好的做法。例如上面對變量t的申明:IP const t;這樣保證程序員不會對t的類型產生誤解。
注意:如果你非要使用typedef來實現const int *t,那么就直接typedef const int *CIP;然后:CIP t;就可以了。