接着之前的內容來說,C和C++的區別。
6、引用
例如:int &b = a;
定義引用變量b,是變量a的引用,即別名。若改變b的值a的值也會改變,因爲a和b指向同一內存單元。注意,&前有類型時爲引用,前邊沒有類型時爲取地址。
引用符號&必須緊跟變量名。在聲明引用變量是必須初始化,即聲明它是誰的引用。初始化的值必須要能夠取地址。初始化之後不能再給引用變量賦值。
float a = 10.5;
int b1 = (int)a;
int b2 = (int)&a;
int b3 = (int&)a;
b1、b2、b3有什麼不同呢?
(int)a 把float類型強轉成int;
(int)&a &a指的是a的地址,前面的int是將十六進制表示的地址強制轉換爲int類型;
(int&)a 將a的引用強制轉換成整型,就是說a所在的內存地址中數據本來是float類型儲存的,引用時非要按int來用。
#include<iostream>
int main()
{
int a = 10;
int &b = a;
int *p = &a;
b = 20;
*p = 20;
cout<<hex<<&a<<" "<<&b<<" "<<&p<<endl;
return 0;
}
通過上面簡單的代碼轉到反彙編如圖所示:
在底層實現上,引用和指針是沒有區別的。
那這裏就有一個問題:定義引用變量到底開不開內存?
答案肯定是開闢,開闢內存儲存所引用變量的地址。訪問引用變量時就是解引用。
打印&a和&b是相同的,也就是說訪問引用變量時自動解引用,訪問的都是被引用的變量的內存,不會訪問到引用變量的地址。所以說引用比指針安全,引用一定會指向有效內存,指針不一定。
接下來是定義引用變量訪問數組名。
int main()
{
int a=10;
int &p = a;
int arr[10] = {0};
//int **q = &arr;//error
int (&p)[10] = arr;
}
數組名取地址是整個數組的地址,所以用數組指針int (*q)[10] = &arr,寫成引用爲int (&p)[10] = arr。
7、const、指針、引用、形參、返回值
const和一級指針的結合
int a= 10;
int *p = &a;
const int *p = &a;//const修飾*p
int const *p = &a;//const修飾*p
int *const p = &a;//const修飾p
const int* const p =&a;//const修飾*p和p
const int a=10;
int *p=&a;//編譯肯定是通不過的,通過*p可能會修改a的值,要杜絕這種風險,所以報錯。
int a=10;
const int*p = &a;//const 修飾*p也就是不會通過解引用修改a的值,在這裏是沒有問題的
int *q = p;//p存放的就是整型常量的地址,const int* 和int*都可以賦給p,如果是const int*就要杜絕,所以這裏也是有錯的。
從上面兩個可以發現,權限變小沒有問題,即int*到const int*;權限變大有問題,即const int*到int*。這裏有一點,修改a的值分爲直接修改(有沒有做左值)和間接修改(常量的指針或引用泄露出去),在C++中做左值是沒有問題的,間接修改纔會報錯。
之前有講到函數重載,那下面的函數是否構成重載呢?
void func(int a);
void func(const int a);//不構成重載
void func(int *a);
void func(int *const a);//不構成重載
void func(int *a);
void func(const int *a);//構成重載
函數重載考慮對實參的值是否有影響,const和volatile修飾的指針或引用可以作爲函數重載的前提條件。
volatile:
1、防止編譯器對彙編指令進行順序上的優化;
2、防止寄存器儲存變量的副本值,主要應用在多線程程序。
對指令的調優:
1、編譯器對指令的調優,volatile防止;
2、運行時CPU對指令的調優,barrier()防止;
const與引用的結合
const int a=10;
int &b=a;//b可能改變a的值,錯誤
但是如果是這樣呢?
const int &b = 10;
這樣是可以的,因爲const引用一個不可尋址的常量時,系統會產生一個臨時量。也就是說上邊的語句等於:
const int tmp = 10;
const int &a = tmp;
const與二級指針的結合
int a=10;
int *p=&a;
const int **p=&p;//const修飾**p,也就是a的值不可改變,但是*p可能改變a的值,錯誤
int a=10;
const int*p=&a;
int **q=&p;//const修飾*p,也就是a的制不能改變,但是**q可能修改a的值,錯誤
//應該是:
int a=10;
const int *p=&a;
const int **p=&p;
int GetInt()
{
int val=10;
return val;
}
int main()
{
int &a=GetInt();//返回值通過寄存器eax帶回,就是相當於帶回一個立即數,int &a=10;錯誤
const int &a=GetInt();//產生臨時量
int *p = &GetInt();//返回值沒地址可取,也是不正確的
return 0;}
int *GetIntptr()
{
static int value = 10;//不能返回局部變量的地址或引用,加static
return &value;//通過寄存器帶回
}
int main()
{
int *p = GetIntptr();//把eax的值放到p中
int *&p = GetIntptr();//GetIntptr()通過寄存器帶回一個立即數,不能取地址
int * const &p = GetIntptr();//函數調用之後產生臨時量,ok
int **p=&GetIntptr();//error,不能取地址
int val=*GetIntptr();//返回的就是地址,ok
int &val=*GetIntptr();//解引用訪問val那塊內存,ok
}
int& GetIntRef()
{
static int tmp=10;
return tmp;//返回tmp的地址
}
int main()
{
int a=GetIntRef();//*eax的值放到a中
int &b=GetIntRef();//可以取地址,ok
int*p = &GetIntRef();//p指向tmp,ok;
}