問題 今天群裡有人發了個問題,問題如下: 第一個賦值可以,為什麼第二個不行 [cpp] int * x = NULL; int const * y = NULL; y = x; int ** z = 0; int const ** n = 0; n = z 當時我懵了一下,隨即想到可能是int const **的問題,便把代碼改成: [cpp] int ** z = 0; int * const * n = 0; n = z; 編譯通過,以為解決了問題,想也沒想,便發回給提問者。 殊不知,提問者問了好幾個問題後,我又懵了,-_-! 這個const是屬於那一邊的? 可以是 [cpp] int (* const) * n = 0; 那麼n就是一個指向(int * const)的int指針 也可以是 [cpp] int * (const *) n = 0; 那麼n就是一個指向(int *)的const int 指針 後來提問者發了書裡面的一句話: 如果const和(或)volatile關鍵字後面緊跟類型說明符(如int, long等),它作用於類型說明符,在其他情況下,const和(或)volatile關鍵字作用於它左邊緊鄰的指針星號。 本人覺得這句話不夠確切,一般情況下,我們會把代碼寫成: [cpp] const int * p; 這和書中描述的一致,const 此時作用於int。但我們也有另外一種寫法 [cpp] int const * p; 雖然大家都知道這個const 必定作用於int的,但按照那句話的方法,const找不到作用的地方了。 好吧,這有點鑽牛角了。既然是從書中出來的,還是得用代碼來證明一下。 試驗 回歸到頂部問題,其實就是類型匹配問題,如果類型不匹配,那麼編譯就會報錯,所以通過編譯器(VS2010)來作為我們的裁判,const到底是作用於誰。 先鋪好兩個必須的變量: [cpp] int * p_to_v_1 = new int(1);; int * p_to_v_2 = new int(2); 第一試驗 [cpp] int * * ppTest1 = &p_to_v_1; ppTest1 = &p_to_v_2; *ppTest1 = p_to_v_2; **ppTest1 = 10; 這個應該理解上沒有多大的問題,所以不解釋。 編譯通過,沒報任何錯誤,結論: *ppTest1也是int * 類型。 **ppTest1是int類型。 第二試驗 [cpp] int const * * ppTest2 = &p_to_v_1; ppTest2 = &p_to_v_2; *ppTest2 = p_to_v_2; **ppTest2 = 10; 這個比較簡單,ppTest2指向的是int const *的指針,所以第三個**ppTest2的賦值編譯器報錯:表達式必須是可修改的左值。 *ppTest2也是int const * (或const int *)類型。 **ppTest2是const int 類型,不可改變,所以賦值時出錯。 第三試驗 [cpp] int * const * ppTest3 = &p_to_v_1; ppTest3 = &p_to_v_2; *ppTest3 = p_to_v_2; **ppTest3 = 10; const 來到兩個星號中間,那麼就是上面我遇到的問題了。 編譯器此時報錯是 *ppTest3 = p_to_v2; 這行代碼,錯誤是:表達式必須是可修改的左值。 p_to_v2是指針變量,那麼*ppTest3就是指針常量 *ppTest3是int * const類型。(印證了書中那句話) **ppTest3那一行沒報錯,那麼**ppTest3就是int類型 第四試驗 [cpp] int * * const ppTest4 = &p_to_v_1; ppTest4 = &p_to_v_2; *ppTest4 = p_to_v_2; **ppTest4 = 10; 報錯的是“ppTest4 = p_to_v2;”這行,錯誤信息和上面例子一樣,那麼ppTest4就是一個指向指針的指針常量,ppTest4是不可更改的。 第五試驗 [cpp] int const * const * ppTest5 = &p_to_v_1; ppTest5 = &p_to_v_2; *ppTest5 = p_to_v_2; **ppTest5 = 10; 有了前面的基礎,應該很容易判斷這裡報錯的是哪個。 沒錯,第3行和第4行錯誤,*ppTest5是指針常量,**ppTest5是常量。 第六試驗 [cpp] int * const const * ppTest6 = &p_to_v_1; ppTest6 = &p_to_v_2; *ppTest6 = p_to_v_2; **ppTest6 = 10; 這個定義看起來有點奇怪,雖然會有編譯警告,但是可以通過的。 報錯的只有第3行(*ppTest6 = p_to_v_2;),因為*ppTest6是指針常量。 為什麼第2行不報錯呢?ppTest6是指向指針常量的指針,&p_to_v_2的結果又是指向指針變量的指針。 因為這個賦值的道理就如const int * 類型可以被int *類型賦值一樣(如函數void f(const int * cp)中的參數,f被調用時,傳入的參數可以是const int*,也可以為int *),一個是指向int常量的指針,一個是指向int變量的指針。 這裡的定義(int (* const) (const *) ppTest6 )還有個地方需要注意一下,剛才說了,ppTest6是指向指針常量的指針;而剛好,*ppTest6又是指針常量,那這個定義就相當於 [cpp] int * const * ppTest6 第七試驗 [cpp] int * const * const ppTest7 = &p_to_v_1; ppTest7 = &p_to_v_2; *ppTest7 = p_to_v_2; **ppTest7 = 10; 2和3行報錯,ppTest7是常量,*ppTest7也是。 第八試驗 [cpp] int const * const * const ppTest8 = &p_to_v_1; ppTest8 = &p_to_v_2; *ppTest8 = p_to_v_2; **ppTest8 = 10; 這個2、3、4都報錯,應該不用怎麼解釋了吧。。。 總結一下,剛才那句話大部分還是對的,就差了我剛才提到的那一點,嘿嘿。 如果const和(或)volatile關鍵字後面緊跟類型說明符(如int, long等),它作用於類型說明符,在其他情況下,const和(或)volatile關鍵字作用於它左邊緊鄰的指針星號。 解決問題 剛才說明了const在不同位置所起的不同作用,現在回過頭來看原先的問題: [cpp] int * x = NULL; int const * y = NULL; y = x; int ** z = 0; int const ** n = 0; n = z 現在來看這些指針,應該比較明確: z就是指向int *的指針; 而n就是指向const int *的指針; n和z最終指向類型(一個是int, 一個是const int)是不一樣的,所以賦不了值。 就如以下代碼 [cpp] int * x = NULL; int * const y = NULL; y = x;