溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

引用的本質(zhì)分析(四)

發(fā)布時間:2020-08-05 01:59:01 來源:網(wǎng)絡(luò) 閱讀:1184 作者:上帝之子521 欄目:編程語言

        我們上節(jié)講了 C++ 中的引用,那么我們就來看下引用的本質(zhì)。引用作為變量別名而存在,因此在一些場合可以代替指針。引用相對于指針來說具有更好的可讀性和實用性。注意:函數(shù)中的引用參數(shù)不需要進(jìn)行初始化!

        下來我們來看看 swap 函數(shù)的實現(xiàn)對比,如下

void swap(int* a, int* b)    // 指針形式的
{
    int t = *a;
    *a = *b;
    *b = t;
}

void swap(int& a, int& b)    // 引用形式的
{
    int t = a;
    a = b;
    b = t;
}

        那么這塊就有個特殊的引用,便是 const 引用了。在 C++ 中可以聲明 const 引用,它的格式為 const Type& name = var;const 引用讓變量擁有只讀屬性。當(dāng)使用常量對 const 引用進(jìn)行初始化時,C++ 編譯器會為常量值分配空間并將引用作為這段空間的別名。使用常量對 const 引用初始化后將生成一個只讀變量!

        下來我們以代碼為例進(jìn)行分析,看看引用的特殊意義,代碼如下

#include <stdio.h>

void Example()
{
    printf("Example:\n");
    
    int a = 3;
    const int& b = a;
    int* p = (int*)&b;
    
    // b = 5;
    
    *p = 5;
    
    printf("a = %d\n", a);
    printf("b = %d\n", b);
}

void Demo()
{
    printf("Demo:\n");
    
    const int& c = 1;
    int* p = (int*)&c;
    
    // c = 5;
    
    *p = 5;
    
    printf("c = %d\n", c);
}

int main(int argc, char *argv[])
{
    Example();
    
    printf("\n");
    
    Demo();
    
    return 0;
}

        我們在 Example 函數(shù)中定義了變量 a,用 b const 引用 a,然后用指針 p 指向 b。然后通過指針 p 改變 b 的值,但是這塊 b 是 const 引用,所以不能直接改變 b。我們看看 a 和 b 會是多少。在 Demo 函數(shù)中,我們通過 const 引用 c 為 1,并且定義指針 p 指向它。同樣不能直接改變 c,但是可以通過指針 p 來改變它的值。我們先來看看通過指針 p 改變后的值是否為 5 呢?看看編譯結(jié)果

引用的本質(zhì)分析(四)

        我們看到值已經(jīng)都改變了,我們再來去掉第 11 和 26 行的注釋,看看直接改變 const  引用會怎樣?引用的本質(zhì)分析(四)

        我們看到報的都是它們是只讀變量。那么我們思考下:引用有自己的存儲空間嗎?我們通過程序來看看

#include <stdio.h>

struct test
{
    char& c;
};

int main(int argc, char *argv[])
{
    char c = 'c';
    char& rc = c;
    test r = { c };
    
    printf("sizeof(char&) = %d\n", sizeof(char&));
    printf("sizeof(rc) = %d\n", sizeof(rc));
    
    printf("sizeof(test) = %d\n", sizeof(test));
    printf("sizeof(r.c) = %d\n", sizeof(r.c));
    
    return 0;
}

        我們在第 3 行定義了一個結(jié)構(gòu)體變量 test,但它里面只有一個 char 類型的引用 c。我們來看看這個結(jié)構(gòu)體占用內(nèi)存嗎?編譯如下

引用的本質(zhì)分析(四)

        我們看到引用本身只占用了一個字節(jié),但是結(jié)構(gòu)體 test 占用了 4 個字節(jié)的內(nèi)存。我們猜想它是不是跟指針有某種聯(lián)系呢?其實引用在 C++ 中的內(nèi)部實現(xiàn)是一個指針常量。關(guān)系如下

引用的本質(zhì)分析(四)

        注意:a> C++ 編譯器在編譯過程中用 指針常量 作為引用的內(nèi)部實現(xiàn),因此引用所占的空間大小與指針相同;b> 從使用的角度,引用只是一個別名,C++ 為了實用性而隱藏了引用的存儲空間這一細(xì)節(jié)。下來我們通過一個示例代碼進(jìn)行說明

#include <stdio.h>

struct TRef
{
    char* before;
    char& ref;
    char* after;
};

int main(int argc, char* argv[])
{
    char a = 'a';
    char& b = a;
    char c = 'c';

    TRef r = {&a, b, &c};

    printf("sizeof(r) = %d\n", sizeof(r));
    printf("sizeof(r.before) = %d\n", sizeof(r.before));
    printf("sizeof(r.after) = %d\n", sizeof(r.after));
    printf("&r.before = %p\n", &r.before);
    printf("&r.after = %p\n", &r.after);

    return 0;
}

        我們看到在結(jié)構(gòu)體 TRef 內(nèi)部只有 3 個成員,兩個指針,一個引用。我們通過打印結(jié)構(gòu)體的大小和它的 before 指針和 after 指針的大小和地址來分別看看中間的引用究竟是什么

引用的本質(zhì)分析(四)

        我們看到結(jié)構(gòu)體總共占 12 個字節(jié)的內(nèi)存,指針 before 和 after 各占 4 個字節(jié),并且他們的地址相差 8,從而雙重說明了中間的引用占 4 個字節(jié)的內(nèi)存空間,引用便是指向一個地址的。那么它的本質(zhì)便是指針了。

        那么為什么還要弄個引用來代替指針呢?我們知道在 C 語言中,凡是涉及到指針的操作都是容易出 bug 的地方,因此 C++ 設(shè)計了引用來在大部分情況下代替指針。從功能性來說,可以滿足大多數(shù)的需要使用指針的場合;從安全性來說,可以避免由于操作指針不當(dāng)而帶來的內(nèi)存錯誤;從操作性來說,簡單易用,又不失功能強大。下面我們來看看函數(shù)返回引用的一個示例

#include <stdio.h>

int& demo()
{
    int d = 0;
    
    printf("demo: d = %d\n", d);
    
    return d;
}

int& func()
{
    static int s = 0;
    
    printf("func: s = %d\n", s);
    
    return s;
}

int main(int argc, char* argv[])
{
    int& rd = demo();
    int& rs = func();
    
    printf("\n");
    printf("main: rd = %d\n", rd);
    printf("main: rs = %d\n", rs);
    printf("\n");
    
    rd = 10;
    rs = 11;
    
    demo();
    func();
    
    printf("\n");
    printf("main: rd = %d\n", rd);
    printf("main: rs = %d\n", rs);
    printf("\n");

    return 0;
}

        我們在 demo 函數(shù)里返回了局部變量 d,因此這個肯定會出問題。在 func 函數(shù)里返回的加 static 修飾的變量,因此它是會放在全局?jǐn)?shù)據(jù)區(qū),不會出錯。我們在第 23 和 24 行用 demo 和 func 函數(shù)進(jìn)行初始化,因此這會打印出 d = 0 和 s = 0;在第 27 和 28 行打印 rd 和 rs 的值,因為 demo 函數(shù)返回之后 d 會丟失,這時 rd 便是一個野指針了。所以 rd 指向的是一個隨機數(shù),但是 rs 還是為 0;第 31 和 32 行分別對 rd 和 rs 進(jìn)行重新賦值,再次調(diào)用 demo 和 func 函數(shù)時,d 還是為 0,s 就為 11 了;最后第 38 和 39 行會打印出 rd 為隨機數(shù),rs 為 11。我們來看看編譯結(jié)果和我們分析的是否一致

引用的本質(zhì)分析(四)

        我們看到它在編譯的時候都已經(jīng)報警告了,打印的結(jié)果和我們所分析的是一致的。通過對引用本質(zhì)的學(xué)習(xí),總結(jié)如下:1、引用作為變量別名而存在旨在代替指針;2、const 引用可以使得變量具有只讀屬性;3、引用在編譯器內(nèi)部使用指針常量實現(xiàn),它的最終本質(zhì)為指針;4、引用可以盡可能的避開內(nèi)存錯誤


        歡迎大家一起來學(xué)習(xí) C++ 語言,可以加我QQ:243343083

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI