? 在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必須寫在&符號的左邊,才能算是常引用
- 常量指針是指指向常量的指針,顧名思義,就是指針指向的是常量,即,它不能指向變量,它指向的內(nèi)容不能被改變,不能通過指針來修改它指向的內(nèi)容,但是指針自身不是常量,它自身的值可以改變, 從而指向另一個常量。
- 指針常量是指指針本身是常量。它指向的地址是不可改變的,但地址里的內(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;

加深記憶記住三句話:
指針和 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'

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'

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 *)