1. 程式人生 > >引用和指針(十)

引用和指針(十)

C++ 指針 引用

前面我們講了引用,這節我們就來看看引用和指針有何區別?又有何相同點。首先我們來看看在 C++ 中,const 什麽時候為只讀變量?什麽時候是常量呢?

關於 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

引用和指針(十)