C和C++區別(二)

接着之前的內容來說,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;


const 指針 引用在函數中的應用


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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章