C++:指標和引用
引用的概念及用法
所謂的引用並不是說重新定義的一個新的變數,而是給一個已經定義好了的變數起的一個別名。
下面看看引用到底是如何使用的:
void test1()
{
int a = 1;
int& b = a; //引用變數b是a的別名
std::cout<<"a:address->"<<&a<<std::endl;
std::cout<<"A:address->"<<&b<<std::endl; //注意這裡的&是取地址
a = 10;
b = 100;
std::cout<<"a = "<<a<<std::endl;
std::cout<<"b = "<<b<<std::endl;
int& c = b; //應用變數c是引用變數b的別名,別名的別名
c = 1000;
std::cout<<"a = "<<a<<std::endl;
std::cout<<"b = "<<b<<std::endl;
std::cout<<"c = "<<c<<std::endl;
}
執行結果如下:
由結果我們可以看出引用變m量b與變數a的地址是一樣的,該變b的值也會影響a。並且一個變數可以取多個別名,這裡b是a的別名,c是b的別名,也就是a的別名了。就像我們人一樣,你有一個大名(身份證上的名字),可能還會有一個小名,也或許還會給自己起一個洋氣的英文名,總之不管別人叫哪一個名字,叫的都是你本人就對了。
總結:
1、一個變數可以有多個別名
2、引用必須初始化b
3、引用只能在初始化的時候引用一次,之後不能再引用其他的變數
引用做引數
在之前的學習當中,我們知道呼叫函式的傳參有傳值呼叫和傳址呼叫。下面再來看一看這兩種方式:
1、傳值呼叫
void swap(int a,in b)
{
int tmp = a;
a = b;
b = tmp;
}
//現在的我們都知道了這樣的函式是無法完成我們希望的交換功能的。
//究其原因,就是因為這裡使用的是傳值方式,那麼如果別人一旦想要
//呼叫我給我傳兩個引數,我就要生成兩個區域性的臨時變數用來接收
//別人給我傳的兩個引數。但是當呼叫結束,函式棧幀釋放,相應的
//用於接收引數的兩個區域性變數也會被一同釋放掉,但是交換是發生在
//這兩個區域性變數之間的,現在他們已經被釋放掉了,並且從頭到尾
//都沒有對呼叫方的兩個想要交換的變數產生任何影響。因此這裡的
//傳值呼叫並完成不了交換的功能。
2、傳址呼叫
void swap(int *a,int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
//所謂的傳址呼叫就是傳指標。
//現在我們將兩個形參改為指標,也就是說別人想要呼叫我完成交換功能時
//就將想要交換的兩個變數的地址傳給我就好了。函式呼叫期間對地址裡面
//的內容進行交換,即便呼叫結束以後,函式棧幀被釋放,但是是對兩個地址
//的內容進行了交換,釋放棧幀以後這兩個地址並不是函式棧幀裡的,
//所以並不會一併被釋放,並且完成了交換的功能。
3、傳引用
void swap(int& a,int& b)
{
int tmp = a;
a = b;
b = tmp;
}
//我們知道引用變數就是我們給一個已經定義好的變數起的一個別名,
//所以說,如果我們這裡採用傳引用的方式,那我們這裡的形參就是實參的別名
//在剛剛我們也看了,變數和變數的別名,他們兩個的地址是同一個,所以啊,
//這和傳址呼叫有著異曲同工之妙。
引用做返回值
有時候我們一個函式呼叫結束需要返回一些資訊供呼叫方使用。比如說一個加法函式。
//方法一
int ADD(int a,int b)
{
int ret = a+b;
return ret;
}
//方法二
int& ADD(int a,int b)
{
int ret = a+b;
return ret;
}
//這裡方法一是採用值的形式返回,而方法2是採用引用的形式返回。
//我們可以看看組合語言是如何這兩種不同返回方式的,如下圖:
那麼問題來了,我們有該如何選擇以那種方式返回呢???
1、如果返回的物件出了該函式作用域依舊存在,則使用引用返回,因為這樣會更加高效
2、如果返回物件處了函式的作用域就不存在了,則使用值返回。
注意:不要返回一個臨時變數的引用,因為臨時變數在函式呼叫結束以後會隨著棧幀的釋放而被釋放,而傳引用返回的方式返回的是變數的地址,而事實是該變數已經被釋放。
引用和指標的區別
在這之前我們一直在說,引用是一個變數的別名,所以可能就會想到說這個引用變數時不會佔據任何的空間的。但是!請注意!這種想法是不對的。引用變數也是會佔據一定的記憶體空間的,也需要在棧上額外佔用儲存空間。
因為引用的底層實現其實是指標。從語法上來看只是一個別名,但在底層上依舊是開闢了一塊空間。
int main()
{
int a = 0;
int& b = a;
return 0;
}
看一下這段程式碼的彙編:是如何處理變數a,和引用變數b
從彙編我們可以看出對引用變數初始化為a的別名,就是將a的地址給了引用變數b。想一想這種方式是不是很熟悉?對了,正如你所想到的我們經常寫的一個程式碼:
int a = 0;
int *p = &a;
//取a的地址賦給指標變數p
這樣看來其實引用的底層也就是一個指標,只不過明面上向我們所展示的是一個變數的別名,但我們應該注意引用變數是一個已經定義過的變數的別名,他是別名,他也佔空間,因為他的底層實現是指標。
下面就看一看引用和指標的區別:
1、引用只能在定義時初始化一次,之後不能改變指向其他的變數,但指標可以。
2、引用必須指向有效的變數,但指標可以為空。
3、sizeof引用得到的是所指向的變數的大小,但sizeof指標是物件的地址的大小。
4、引用的自增(+ +)自減(- -)是對值的+1或-1,而指標++或–是+或-其所指向的型