1. 程式人生 > >C語言------------c++中的引用(引用和多級指標的對比)

C語言------------c++中的引用(引用和多級指標的對比)

說明:C語言中是沒有引用的,c++中才有引用,本人的C語言的程式碼的檔案字尾名是  .cpp。原因是c++語言的編譯器相容C語言

如果字尾名寫成.c那麼會有好多不便,尤其是老版本的c語言的編譯器更是用起來氣人。

引用

        定義:為物件起了另外一個名字,引用型別去引用另一種型別,通過將宣告符寫成&d的形式來定義引用型別,其中d是宣告的引用型別的變數名&d:表示引用變數中儲存的內容(即引用所指向的變數的地址);d:表示以引用變數中儲存的內容為地址的變數中的值。引用變數和指標變數一樣都是存的所指向變數的地址,不同點指標自身變數自身有地址,而引用變數沒有地址(系統為引用變數自身分配了地址,只不過引用變數自身的地址我們連讀的許可權都沒有,所以我們預設引用自身沒有地址,即引用類似常量),所不能宣告引用的引用,也不能宣告指向引用的指標,但是可以宣告指向指標的指標,也可以宣告指向指標的引用

引用的實質:從彙編的角度來看,當使用到一個變數的引用時,編譯器就把變數的地址(即引用)直接壓入堆疊,函式從堆疊中取出這個地址就可以操作這個變量了。所以說引用不是變數是對的,這只是記憶體空間的一個地址。引用的作用機理

但是計算機真的就沒有給引用用變數分配記憶體空間嗎?其實引用變數系統是分配了記憶體空間的:當引用變數有所指向的時候,他就相當於一個操作受限的指標,引用的底層使用指標常量實現了讓我們訪問不到引用變數本身的地址,&r:表示引用變數中儲存的內容(即引用所指向的變數的地址或者說被引用的目標變數的變數地址),r表示被引用目標變數的值。

&:什麼時候當做引用識別符號,什麼時候當做取地址符

&出現在宣告中時&當做是引用宣告符,其他情況當做取地址符例如 i = 10;  int &r = i;此時&是一個引用宣告符

                            分析兩端段程式碼:

                              int i= 10;

                              int j = 20;

                              int &r = i;

                               r = j;//引用變數的r實際是以引用變數r中存的內容為地址的變數中的值,實質r就是i

                               程式碼二

                                int a = 10;

                                int *p = &a;

                                *P =5;//*運算是以指標變數p中存的內容為地址的變數中的值

引用系統為其分配記憶體空間,引用變數和指標變數一樣都是存址(引用變數中儲存的是他所指向的變數的地址)

#include<stdio.h>
int main()
{
	char k = 'a';
	int i = 10;
	double j = 20.0;


	
    char &r = k;
	char* p = &k;

	int &r1 = i;//引用
	int *p1 = &i;

	double &r2 = j;
	double *p2 = &j;


	
	printf("======存地址的變數系統會分配4個位元組===\n");
	printf("char型別的引用變數分配了%d個位元組\nchar*型別的指標變數分配了%d個位元組\n",sizeof(&r),sizeof(p));
	printf("int型別的引用變數分配了%d個位元組\nint*型別的指標變數分配了%d個位元組\n",sizeof(&r1),sizeof(p1));
	printf("double型別的引用變數分配%d個位元組\ndouble*型別的指標變數分配%d個位元組\n\n",sizeof(&r2),sizeof(p2));
    printf("===引用變數和指標變數中存的都是他們所指向的變數的地址\n");
	printf("&k = %Xf\t&r = %Xf\tp = %Xf\n",&k,&r,p);
	printf("&i = %Xf\t&r1 = %Xf\tp1 = %Xf\n",&i,&r1,p1);
	printf("&j = %Xf\t&r2 = %Xf\tp2 = %Xf\n\n",&j,&r2,p2);

	printf("===引用變數名與指標變數*運算後的結果相同===\n");
	printf("k = %c\t\tr = %c\t\t*p = %c\n",k,r,*p);
	printf("i = %d\t\tr1 = %d\t\t*p1 = %d\n",i,r1,*p1);
	printf("j = %lf\tr2 = %lf\t*p2 = %lf\n",j,r2,*p2);
	return 0;

}

執行結果:

1、引用型別的的變數在宣告的同時必須初始化,因為引用似常量所以必須宣告時初始化,更沒有引用的引用即沒有int && i = &j,指標卻有指標的指標,因為指標自身的地址我們有讀的許可權)

#include<stdio.h>
int main()
{
	int i = 10;
	/*
	int &r ;//error C2530: 'r' : references must be initialized引用宣告時必須初始化
	r = i;
	*/
        int &r = i;//正確
	return 0;
}

2、引用的賦值:一般初始化變數時,初始值會被拷貝到新建的物件中,然而定義引用時,程式把引用和它的
初始值繫結在一起,而不是將初始值拷貝給引用,引用即被繫結變數的別名。引用並不是物件(即引用型別的
變數自身的地址我們無法進行讀寫操作,然後我們就說引用型別變數沒有地址(它裡面存的是繫結的變數的地址))

相反的,它只是為一個已經存在的物件所起的另外一個名字

#include<stdio.h>
int main()
{
	int i = 10;
        int j = 20;
        int &r = i;
        r = j;//此時引用(指向)了一個整形變數,此時給r賦值,就是給i賦新值。此時i中的值也是j(即20)
	      //換句話說把變數j的值賦值給以引用變數i中所存放的內容為地址的變數
	printf("i = %d\n",i);
	return 0;
}

執行結果:

2-1、通過函式傳參可以實現通過被調函式為主調函式中的變數賦值。形參為引用型別(效率比用指標好)

#include<stdio.h>
void rfun(int &r);
int main()
{
	int i = 10;
	printf("i = %d\n",i);

	printf("======呼叫函式後i的值為======\n");

	rfun(i);//通過函式傳參來修改主調函式中的變數
	printf("i = %d\n",i);
	return 0;

}
void rfun(int &r)
{
	r = 20;//把20賦值給以引用變數r中儲存的內容為地址的變數
}

執行結果:

***此處經常變換成函式傳引用來代替和普通變數中的函式傳值。(如果不希望被調函式改變主調函式

中的變數的值時可使用const關鍵字。例如函式僅僅用來列印主函式中的資訊)

3、一旦初始化完成,引用將和它的初始值一直綁在一起,因為無法將引用繫結到另一個物件,因為引用沒有地址,說明他恆定不變,那他就擁有常量的性質,常量宣告時必須初始化,而且只能初始化一次

#include<stdio.h>
int main()
{
	int i = 10;
	int j = 20;
	int &r = i;
	//int &r = j;//報錯error C2374: 'r' : redefinition; multiple initialization不能重複初始化
	return 0;
}

 4、因為引用本身不是一個物件,引用變數沒有實際的地址,所以不能定義引用的引用&r(而指標是一個物件,

即指標變數有地址,可以定義指向指標的指標)例如:

#include<stdio.h>
int main()
{
	int i = 10;
	int &r = i;
//	int &&r1 = &r;報錯不能宣告引用的引用,因為引用本身沒有地址&r是引用變數中儲存的內容(它是引用所指向的變數的地址)
	return 0;
}

      ***通常什麼樣的型別的變數自身有地址,我們就可以定義什麼樣型別的引用變數來繫結那個變數,也可以定義什麼樣型別的指標來指向那個變數。

      ***什麼樣型別的變數可以宣告為常量?:自身有地址我們把他自身的地址弄成恆定不變那麼他就成常量了,或者或什麼樣型別的變數變數有地址,但是我們訪問不到這時候我們說這種型別自身的地址就是恆定不變,例如引用,那麼我們也可以把這種型別宣告為常量。(《c++primer》中說的)(下一篇討論)

***5、我們可以定義指向指標的引用(定義一個引用變數來繫結一個指標變數)int *&引用變數名 = 值真變數名;(一級指標引用)int** &r =*p;(二級指標引用);

       基礎概念(重要的地方說三遍):   int i = 10;

                                                         int *p = &i;//指標

                                                         int j = 20;

                                                         int &r = j;//引用

        p:表示指標變數p中所存的值(此處為變數i的地址)

        &p:表示指標變數p本身的地址(上一篇指標之多級指標中有介紹)

        *p:表示以指標變數p中儲存的內容為地址的變數的值(此處為變數i的值)

        &r:表示引用變數r中所存放的值(此處為變數j的地址)

        r:表示以引用變數r中儲存的內容為地址的變數的值(此處為變數j的值)

5.1在指向指標的引用中的各個變數的地址和變數值的關係分析

#include<stdio.h>
int main()
{
	int i = 10;
	int* p = &i;
	int* &r = p;//把指標變數p本身的地址(&p)賦值給引用變數r,此時引用變數r中


   	printf("引用變數中存的值為:   &r = %Xf\n指標變數本身的地址為: &p = %Xf\n\n",&r,&p);
	printf("======華麗的分割線======\n\n");
	printf("變數i的地址為:\t         &i = %Xf \n指標變數p中存的值為:      p = %Xf\n引用變數所引用的變數的值: r = %Xf\n",&i,p,r);
	return 0;
}

執行結果:

      分析圖:引用變數r的地址掌握在編譯器手中,程式設計師無法讀取到,&r裡面儲存的是被引用的目標變數的地址,變數r裡面儲存的是以被引用的目標變數的地址為地址的值。

5.1、通過操作指向指標的引用的變數,從而達到改變指標所指向的變數的值

#include<stdio.h>
int main()
{
	int i = 10;
	int* p = &i;
	int* &r = p;
	printf("i = %d\n",i);
	*r = 30;//實際因引用變數中存放的內容為地址的變數中的值(即真變數p中存放的值)在進行*運算
	printf("通過改變指向指標變數的引用的引用變數,來改變指標所指向的變數的值:i = %d\n",i);
	return 0;
}

執行結果:

5.1.2、用函式實現通過操作指向指標的引用的變數,從而達到改變指標所指向的變數的值。實參是指標變數形參是指向指標的引用

#include<stdio.h>
void fun(int* &r)
{
	*r = 30;//引用變數就是指標變數的一個別名,【最重點的語句】
}

int main()
{
	int i = 10;
	int*p = &i;
	printf("i = %d\n",i);
	fun(p);
	printf("用函式實現通過改變指向指標的引用的變數,從而達到改變指標所指向的變數的值:i = %d\n",i);
	return 0;
}

執行結果:

5.2、通過操作指向指標的引用的變數,從而達到改變指標變數中的值

#include<stdio.h>
#include<malloc.h>

int main()
{
	int *p = NULL;
	printf("指標變數的初始值為: p = %X\n",p);
        int * &r = p;//【重點語句】
	r = (int*)malloc(sizeof(int)*4);【重點語句】
	printf("通過操作指向指標的引用變數來改變指標變數中的值: p = %X\n",p);
	printf("======華麗的分割線======下面是釋放記憶體這次不討論===\n");
	free(p);//釋放動態記憶體
	p = NULL;
	if(p != NULL)
	{
		printf("%d\n",*p);
	}
	return 0;
}

執行結果:

5.2.1呼叫函式通過操作指向指標的引用的變數,從而達到改變指標變數中的(值實參為指標變數形參為指向指標的引用)

#include<stdio.h>
#include<malloc.h>
void fun(int* &r);//函式的宣告此函式的功能是開闢動態記憶體的
void funDest(int* &r);//釋放記憶體
int main()
{
	int *p = NULL;
	printf("指標變數的初始值為: p = %X\n",p);
	fun(p);
	printf("通過操作指向指標的引用變數來改變指標變數中的值: p = %X\n",p);
	funDest(p);//釋放記憶體
	return 0;
}
void fun(int *& r)
{
	r = (int*)malloc(sizeof(int)*4);//引用變數r是主調函式中指標變數p的一個別名【重點語句】
	printf("我是在函式中分配的動態記憶體的首地址: r = %X\n",r);
}
void funDest(int* &r)
{
	free(r);
	r = NULL;
}

執行結果:

6.1、通過指向指標的指標即多級指標也能改變指標變數中存的值,只不過沒有指向指標的引用好用,實參指標變數的地址,形參指向指標的指標

#include<stdio.h>
#include<malloc.h>
int main()
{
	int* p = NULL;
	int**q = &p;//二級指標用於指向一級指標【重點語句】

	printf("指標變數p中存放的地址為: %X\n",p);

	*q = (int*)malloc(sizeof(int)*4);//通過二級指標給一級指標變數賦值【重點語句】
	**q = 12;【重點語句不懂可以看上一篇中指標中的多級指標】

	printf("malloc函式申請的動態記憶體首地址為: %X\n",*q);
	printf("指標變數p中存放的地址為: %X\n",p);
	printf("======下面列印的結果是關於記憶體釋放傳哪個值=====傳哪個都一樣\n");
	printf("**q = %d\t*p = %d\n",**q,*p);
	//free(*q);
	free(p);
	printf("%d\t%d\n",**q,*p);
	printf("=====列印的值為-572662307表示已經釋放===\n");

	return 0 ;
}

執行結果:

6.1.1呼叫函式通過操作多級指標從而來改變一級指標變數中的值

#include<stdio.h>
#include<malloc.h>
void fun(int**q);//宣告開闢記憶體的函式
void funDest(int**q);//宣告釋放動態記憶體的函式
int main()
{
	int *p =NULL;
	printf("主調函式指標p原來的值: %X\n",p);
	fun(&p);
	printf("通過函式修改主調函式中指標變數的值後,主調函式中指標變數p的值為: p = %X\n",p);
        fun(&p);//釋放記憶體

	return 0;
}
void fun(int**q)    //通過此函式來改變主函式中的指標變數的值
{
	*q = (int*)malloc(sizeof(int));//以q中存放的內容(指標變數p的地址&p)為地址的變數的值(即指標變數p中的值)【重點語句】
	printf("通過函式申請的記憶體首地址為:*q = %X\n",*q);
}
void funDest(int**q)
{
	free(*q);////釋放的是以q中存的內容為地址(即指標變數p的地址)的變數的值(即指標變數p的值)
}

執行結果:


  不能定義指向引用的引用
  也不能定義指向引用的指標

6、&運算子和*運算子的討論

&出現在宣告中的&是引用宣告符,其他情況當做取地址符

*出現在指標變數前面叫做:對指標變數進行*運算

*出現在一個指向指標的引用的引用變數的前面叫做:對引用變數進行解引用

7,指向陣列的引用;

未完待續:

參考資料:《c++primer》