指針和引用

? 在C語言中,使用指針(Pointer)可以間接獲取、修改某個變量的值

? 在C++中,使用引用(Reference)可以起到跟指針類似的功能

void funRef(int &ref){
    ref++;
}

int main(){
    //定義int類型變量
    int var = 0x41;
    //int指針變量,初始化為變量var的地址
    int *pnVar = &var;
    //取出指針pcVar指向的地址內(nèi)容并顯示
    char *pcVar = (char*)&var;
    printf("%s",pcVar);

    //引用作為參數(shù),即把var的地址作為參數(shù)
    funRef(var);
    
    int age = 10;
    // *p就是age的別名
    int *p = &age;
    *p = 30;
    // ref就是age的別名
    int &ref = age;
    ref = 30;

    return 0;
}

? 注意點

  • 引用相當于是變量的別名(基本數(shù)據(jù)類型、枚舉、結(jié)構(gòu)體、類、指針、數(shù)組等,都可以有引用)

  • 對引用做計算,就是對引用所指向的變量做計算

int main() {
   //cout << sizeof(Student) << endl;

   int age = 10;

   // *p就是age的別名
   int *p = &age;
   *p = 30;

   // ref就是age的別名
   int &ref = age;
   ref += 30;
   cout<< age << endl;
 }

60

  • 在定義的時候就必須初始化,一旦指向了某個變量,就不可以再改變,“從一而終

  • 可以利用引用初始化另一個引用,相當于某個變量的多個別名

void test() {
   int age = 10;

   int height = 20;

   // 定義了一個age的引用,ref相當于是age的別名
   int &ref = age;
   int &ref1 = ref;
   int &ref2 = ref1;

   ref += 10;
   ref1 += 10;
   ref2 += 10;

   cout << age << endl;
}

40

? 引用存在的價值之一:比指針更安全、函數(shù)返回值可以被賦值

比指針更安全

引用在開發(fā)中的使用Demo

//void swap(int *v1, int *v2) {
//  int tmp = *v1;
//  *v1 = *v2;
//  *v2 = tmp;
//}
/*
后面還可以再用別的值調(diào)換嗎?不是說引用“從一而終”嗎
*/

void swap(int &v1, int &v2) {
   int tmp = v1;
   v1 = v2;
   v2 = tmp;
}

void test2() {
   int a = 10;
   int b = 20;
   // swap(&a, &b);
   swap(a, b);
   cout << "a = " << a << ", b = " << b << endl;

   int c = 2;
   int d = 3;
   swap(c, d);
   cout << "c = " << c << ", d = " << d << endl;
}

a = 20, b = 10

c = 3, d = 2

double &refD = age;
類型不匹配

引用的本質(zhì)

? 引用的本質(zhì)就是指針,只是編譯器削弱了它的功能,所以引用就是弱化了的指針

int age = 10;

// *p就是age的別名
int *p = &age;
*p = 30;

// ref就是age的別名
int &ref = age;
ref += 30;
cout << sizeof(p) << endl;
cout << sizeof(ref) << endl;

8 指針的sizeof 大小和運行環(huán)境有關(guān)系,我現(xiàn)在用的macos 64位編譯,所以一個指針大小為 8
4 這里為什么輸出來為4 其實輸出的是sizeof(int) 的大小 int在C++中,4位大小

? 一個引用占用一個指針的大小

struct Student {
    int age;
};

cout << sizeof(Student) << endl;

4

struct Student {
    int *age;
};
cout << sizeof(Student) << endl;

8

struct Student {
   int &age;
};
cout << sizeof(Student) << endl;

8

側(cè)面證明了一個引用占用一個指針的大小

使用匯編來窺探引用的本質(zhì)

匯編代碼
_p$ = -12                                         ; size = 4
_ref$ = -8                                          ; size = 4
_age$ = -4                                          ; size = 4
_main   PROC
        push    ebp
        mov     ebp, esp
        sub     esp, 12                             ; 0000000cH
        mov     DWORD PTR _age$[ebp], 10      ; 0000000aH
        lea     eax, DWORD PTR _age$[ebp]
        mov     DWORD PTR _p$[ebp], eax
        mov     ecx, DWORD PTR _p$[ebp]
        mov     DWORD PTR [ecx], 30                 ; 0000001eH
        lea     edx, DWORD PTR _age$[ebp]
        mov     DWORD PTR _ref$[ebp], edx
        mov     eax, DWORD PTR _ref$[ebp]
        mov     ecx, DWORD PTR [eax]
        add     ecx, 30                             ; 0000001eH
        mov     edx, DWORD PTR _ref$[ebp]
        mov     DWORD PTR [edx], ecx
        xor     eax, eax
        mov     esp, ebp
        pop     ebp
        ret     0
_main   ENDP
匯編代碼

從匯編語言來看,引用和指針生成的匯編代碼一致,所以說 引用的本質(zhì)就是指針

看一下指針地址賦值的過程

? lea dest, [ 地址值 ]

  • 將地址值賦值給dest,類似于dest = 地址值
int main(){
    int age = 3;
    // *p就是age的別名
    int *p = &age;
    *p = 5;
}
指針地址賦值
// eax == ebp-0Ch,存放著age的地址值
008519C9  lea         eax,[ebp-0Ch]  

// ebp-18h是指針變量p的地址值
// 將age的地址值存放到指針變量p所在的存儲空間
// int *p = &age;
008519CC  mov         dword ptr [ebp-18h],eax  

lea 是沒有單位的,move設(shè)計到把地址的值取出來,所以是需要單位的 dword 表示4個字節(jié)

所以看指針匯編的標志性代碼是 lea

引用地址賦值

再看把指針修改為引用 ,匯編代碼完全一樣, 所以說引用的本質(zhì)就是指針

引用相當于是變量的別名(基本數(shù)據(jù)類型、枚舉、結(jié)構(gòu)體、類、指針、數(shù)組等,都可以有引用

  • 結(jié)構(gòu)體的引用
//結(jié)構(gòu)體也可以是引用類型
struct Date {
   int year;
   int month;
   int day;
};

Date d = {2011, 1, 5};

Date &ref = d;
ref.day = 2014;
  • 指針的引用
int age = 10;
int *p = &age;
int *&ref = p;
*ref = 30;
  • 數(shù)組的引用
int array[] = {1, 2, 3};
int (&ref)[3] = array;
int *p;
    // 指針數(shù)組,數(shù)組里面可以存放3個int *
    int *arr1[3] = {p, p, p};
// 用于指向數(shù)組的指針
int (*arr2)[3];//數(shù)組指針
int (&ref)[3] = array;
int (*arr2)[3];

不存在【引用的引用、指向引用的指針、引用數(shù)組】

引用的引用

常引用(Const Reference)

? 引用可以被const修飾,這樣就無法通過引用修改數(shù)據(jù)了,可以稱為常引用

  • const必須寫在&符號的左邊,才能算是常引用
  1. 常量指針是指指向常量的指針,顧名思義,就是指針指向的是常量,即,它不能指向變量,它指向的內(nèi)容不能被改變,不能通過指針來修改它指向的內(nèi)容,但是指針自身不是常量,它自身的值可以改變, 從而指向另一個常量
  2. 指針常量是指指針本身是常量。它指向的地址是不可改變的,但地址里的內(nèi)容可以通過指針改變。它指向的地址將伴其一生,直到生命周期結(jié)束。有一點需要注意的是,指針常量在定義時必須同時賦初值。
int height = 20;
int age = 10;
// p2可以修改指向,不可以利用p2間接修改所指向的變量
int const *p2 = &age;
p2 = &height;
// *p2 = 30;
// ref1不能修改指向,但是可以通過ref1間接修改所指向的變量
const int &ref1 = age;
const int *p = &age
//ref1 = 30;
//*p = 30;
  • 'const' qualifier may not be applied to a reference
int & const ref3 = age;
image-20210327195723638

加深記憶記住三句話:

指針和 const 誰在前先讀誰 ;

*象征著地址,const象征著內(nèi)容;

誰在前面誰就不允許改變。例如 int const p1 = &b 常量指針 const在前,所以內(nèi)容不能修改,即 (p1 = 30)錯誤

int const p2 = &c 指針常量 (指針)在前,所以指針**不能修改 (p2=&d)錯誤

? const引用的特點

  • const引用可以指向臨時數(shù)據(jù)(常量、表達式、函數(shù)返回值等)

Non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'

image-20210327201338747
const int &refError = 30;
int func() {
   return 8;
}
const int &ref = func();//函數(shù)返回值
  • const引用可以指向不同類型的數(shù)據(jù)

Non-const lvalue reference to type 'double' cannot bind to a value of unrelated type 'int'

image-20210327201748830
int age = 10;
const double &ref = age;
  • const引用作為函數(shù)參數(shù)時(此規(guī)則也適用于const指針)

? 可以接受const和非const實參(非const引用,只能接受非const實參)

? 可以跟非const引用構(gòu)成重載

int sum(int &v1, int &v2) {
   cout << "sum(int &v1, int &v2)" << endl;
   return v1 + v2;
}

int sum(const int &v1, const int &v2) {
   cout << "sum(const int &v1, const int &v2)" << endl;
   return v1 + v2;
}

void test2() {
   // 非const實參
   int a = 10;
   int b = 20;
   sum(a, b);

   // const實參
   const int c = 10;
   const int d = 20;
   sum(c, d);

   sum(10, 20);
}

sum(int &v1, int &v2)
sum(const int &v1, const int &v2)
sum(const int &v1, const int &v2)

? 當常引用指向了不同類型的數(shù)據(jù)時,會產(chǎn)生臨時變量,即引用指向的并不是初始化時的那個變量

int age = 10;

const int &rage = age;

age = 30;

cout << "age is "<< age << endl;
cout << "rage is "<< rage << endl;

age is 30
rage is 30

int age = 10;

const long &rage = age;

age = 30;

cout << "age is "<< age << endl;
cout << "rage is "<< rage << endl;

age is 30
rage is 10

匯編代碼
_rage$ = -12                                            ; size = 4
_$S1$ = -8                                          ; size = 4
_age$ = -4                                          ; size = 4
_main   PROC
        push    ebp
        mov     ebp, esp
        sub     esp, 12                             ; 0000000cH
        mov     DWORD PTR _age$[ebp], 10      ; 0000000aH
        mov     eax, DWORD PTR _age$[ebp]
        mov     DWORD PTR _$S1$[ebp], eax
        lea     ecx, DWORD PTR _$S1$[ebp]
        mov     DWORD PTR _rage$[ebp], ecx
        mov     DWORD PTR _age$[ebp], 30      ; 0000001eH
        xor     eax, eax
        mov     esp, ebp
        pop     ebp
        ret     0
_main   ENDP

產(chǎn)生了臨時變量 _$S1$

mov eax, DWORD PTR _age$[ebp] 把age的值放入寄存器 eax

mov DWORD PTR _$S1$[ebp], eax eax的值放入_$S1所在的內(nèi)存地址

lea ecx, DWORD PTR _$S1$[ebp]把$S1的內(nèi)存地址給ecx

mov DWORD PTR _rage$[ebp], ecx把ecx存的值放入_rage地址中

從這段匯編代碼可以看出 常引用指向了不同類型的數(shù)據(jù)時,會產(chǎn)生臨時變量

數(shù)組的引用

? 常見的2種寫法

   // 數(shù)組名arr其實是數(shù)組的地址,也是數(shù)組首元素的地址
   // 數(shù)組名arr可以看做是指向數(shù)組首元素的指針(int *)
int arr[] = {1, 2, 3};
int (&ref)[3] = arr;
int * const &ref2 = arr;
  • 數(shù)組名arr可以看做是指向數(shù)組首元素的指針(int *)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容