引用和指針(十)
關於 const 常量的判別準則有這麽幾個:1、只有用字面量初始化的 const 常量才會進入符號表;2、使用其它變量初始化的 const 常量仍然是只讀變量;3、被 volatile 修飾的 const 常量不會進入符號表。凡是在編譯期間不能直接確定初始值的 const 標識符,都被作為只讀變量處理。const 引用的類型與初始化變量的類型相同時,初始化變量成為只讀變量;不同時,生成一個新的只讀變量!
下來我們以代碼為例進行分析
#include <stdio.h> int main() { const int x = 1; const int& rx = x; int& nrx = const_cast<int&>(rx); nrx = 5; printf("x = %d\n", x); // 1 printf("nx = %d\n", rx); // 5 printf("nrx = %d\n", nrx); // 5 printf("&x = %p\n", &x); printf("&nx = %p\n", &rx); printf("&nrx = %p\n", &nrx); volatile const int y = 2; int*p = const_cast<int*>(&y); *p = 6; printf("y = %d\n", y); // 6 printf("p = %p\n", p); const int z = y; *p = 7; printf("z = %d\n", z); // 6 printf("p = %p\n", p); char c = 'c'; char& rc = c; const int& trc = c; rc = 'a'; printf("c = %c\n", c); // a printf("rc = %c\n", rc); // a printf("trc = %c\n", trc); // c return 0; }
我們來分析下這個程序,在第 5 行定義了一個 const 修飾的 x,所以 x 會進入符號表,它為常量,但是編譯器仍然會在棧上為它分配 4 字節的空間。第 6 行是 const 修飾的引用,所以 rx 只是一個具有只讀屬性的變量,通過第 8 行的 const_cast 去除 rx 的只讀屬性,在第 10 行改變了 nrx 的值,所以第 12 - 14 行應該打印出 1, 5, 5。第 16 - 18 行打印出的三個地址值應該是相同的,因為它們都是引用。第 20 行定義的由 volatile const 修飾的變量 y 在本質上也是一個具有只讀屬性的變量,所以通過第 21 行的 const_cast 會去掉它的只讀屬性,通過第 23 行的指針操作,會改變它的值。所以第 25 行應該打印出 6。第 28 行定義的 const int z,由於它指向的 y 是被 volatile 修飾的,所以在讀取 z 的值時會去 y 的地址空間裏讀取,通過第 30 指針操作並不會改變它的值,第 32 行應該打印出 6。在第 36 行通過 rc 引用了 c,所以他倆是同一個東西。因為第 37 行的引用是不同類型間的引用,所以它會始終指向的是 c 第一次定義的值。我們通過第 39 行的指針操作,會改變 c 和 rc 的值,但並不會改變 trc 的值。我們來看看編譯結果
我們看到編譯的結果和我們所分析的是一致的,證明我們的分析是正確的。
下來我們來看看引用和指針的關系,為何理解我們之前所說的“引用的本質就是指針常量”。1、指針是一個變量:a> 值為一個內存地址,不需要初始化,可以保存不同的地址;b> 通過指針可訪問對應內存地址中的值;c> 指針可以被 const 修飾成為常量或者只讀變量;2、而引用只是一個變量的名字:a> 對引用的操作(賦值,取地址等)都會傳遞到代表的變量上;b> const 引用使其代表的變量具有只讀屬性;c> 引用必須在定義時初始化,之後無法代表其它變量。
從使用 C++ 語言的角度來看:a> 引用於指針沒有任何的關系。b> 引用是變量的新名字。 c> 操作引用就是操作對應的變量;從 C++ 編譯器的角度來看:a> 為了支持新概念“引用 ”必須要一個有效的解決方案。 b> 在編譯器內部,使用指針常量來實現“引用”。 c> 因此,“引用”在定義時必須初始化。
在工程項目開發中:當進行 C++ 編程時,直接站在使用的角度看待引用,與指針毫無關系,引用就是變量的別名;當對 C++ 代碼進行調試分析時,一些特殊情況,可以考慮站在 C++ 編譯器的角度看待引用。
下來我們以代碼為例進行分析
#include <stdio.h> int a = 1; struct SV { int& x; int& y; int& z; }; int main() { int b = 2; int* pc = new int(3); SV sv = {a, b, *pc}; //int& array[] = {a, b, *pc}; printf("sv.x = %d\n", sv.x); printf("sv.y = %d\n", sv.y); printf("sv.z = %d\n", sv.z); printf("&sv.x = %d\n", &sv.x); printf("&sv.y = %d\n", &sv.y); printf("&sv.z = %d\n", &sv.z); delete pc; return 0; }
我們在裏面定義了一個結構體引用。在第 16 行賦值 a, b, *pc。那麽我們打印的結構體裏的值就應該為 1,2,3。我們來看看編譯結果,看看這樣的引用是否被支持
我們再來用引用初始化數組看看是否被支持呢?去掉第 17 行的註釋
我們看到它報錯了,那麽為什麽它支持結構體的引用,卻不支持數組的引用呢?別忘了它是要兼容 C 語言的特性,在 C 語言中,數組間的每個元素都是連續的,相鄰元素的地址差值應該為 4,可是我們的引用的是不同地址處的元素,當然不符合這個性質了。所以在 C++ 中,引用除了數組,其他的都支持。通過對指針和引用的學習,總結如下:1、指針是一個變量,引用只是一個變量的新名字;2、const 引用能夠生成新的只讀變量;3、在編譯器內部使用指針常量實現“引用”;4、編譯時不能直接確定初始值的 const 標識符都是只讀變量。
歡迎大家一起來學習 C++ 語言,可以加我QQ:243343083。
引用和指針(十)