1. 程式人生 > >C++ 指標與引用

C++ 指標與引用

一:指標

1.1:指標詳解

變數的地址的概念: 記憶體是以位元組為單位進行編址的,記憶體中的每個位元組都對應一個地址,通過地址才能找到每個位元組。變數對應記憶體中的一段儲存空間,該段儲存空間佔用一定的位元組數,用這段儲存空間的第一個位元組的地址表示變數的地址。

指標的概念: 指標是一個變數,與普通變數不同的是,普通變數中儲存的是資料,指標變數中儲存的是另一個變數的地址。這樣我們就可以通過指標間接的對普通變數進行操作。

將指標變數加1後,其增加的值等於指向的型別佔用的位元組數

指向普通變數的指標2種初始化方法: 方法一: int a = 0; int *p = &a; *p = 3; p:所指向的變數a的地址 &p:該指標變數p的地址 *p:所指向變數a的值 變數記憶體位於棧(stack)區

方法二: int *p = new int; *p = 100; delete p; 變數記憶體位於堆(heap)區

1.2:指標與陣列

陣列的動態聯編(執行時分配空間,陣列長度在執行時確定) int * p = new int[3]; p[0] = 1; p = p + 1; delete [] p; p[1] = *(p + 1)

靜態聯編(陣列長度編譯時確定) int arr[3] = {1,2,3}; int *p = &arr[0]; int *p = arr; arr[2] = *(arr + 1);

陣列名和指標等價,都是地址;(陣列名指的是第一個元素的地址) 區別: 1、指標值可以修改,陣列名不行; 2、sizeof對於陣列是陣列的長度,對於指標是指標的長度

arr[i] == *(ar_ptr + i) &arr[i] == ar_ptr + i

指向一維陣列的陣列指標 int a[10] = {0}; int *p = a; p[i]; p++; p+i; 注意:只有當指標指向一串連續的儲存單元時,指標的移動才有意義。才可以將一個指標變數與一個整數n做加減運算。 陣列名代表陣列(元素)的首地址,即第一個元素的地址。對陣列的首地址加上偏移量x就可以得到其它元素的地址。

指標陣列,內部全部儲存指標; int* arr[10];

指向二維陣列的指標 int arr[3][4]; int (*p)[4] = arr;

void fun(int *) 等效於 void fun(int arr[]) 使用const修飾,可以避免修改陣列值 void fun(const int arr[])

1.3:指標的指標

二維/二級指標,指標的指標(int**p) int a = 12; int *b = &a; int **c = &b;

a 12 b &a *b a、12 c &b *c b、&a **c *b、a、12

1.4:懸掛指標(野指標)

指向非法的記憶體地址(垃圾記憶體的地址),那麼這個指標就是懸掛指標,也叫野指標,意為無法正常使用的指標

野指標的成因主要有3種: 1、指標變數沒有被初始化。 任何指標(全域性指標變數除外,全域性指標變數為NULL)變數在剛被建立的時候不會自動成為NULL指標,它的預設值是隨機的。所以指標變數在建立的時候,要麼設定為NULL,要麼指向合法的記憶體。 2、指標p被free/delete之後,沒有置為NULL(最好加一句p = NULL;),經常性的我們會以為p是個合法的指標。他們只是把指標指向的記憶體給釋放掉,並沒有把指標本身幹掉,此時指標指向的就是“垃圾”記憶體。所以我們應該在釋放完之後,立即將指標置為NULL,防止出現亂指的情況 3、 指標操作超越了變數的作用範圍。不要返回指向棧記憶體的指標或引用,因為棧記憶體在函式結束時會被釋放。也就是說指向的物件的宣告週期已經結束了,然而我們還使用該指標訪問該物件

二:引用

2.1:引用詳解

概念:: 1、引用:就是某一變數(目標)的一個別名,對引用的操作與對變數直接操作完全一樣。 2、引用的宣告方法:型別識別符號 &引用名=目標變數名; 3、定義一個引用時,必須對其初始化 4、引用僅是變數的別名,而不是實實在在地定義了一個變數,因此引用本身並不佔用記憶體,而是和目標變數共同指向目標變數的記憶體地址 5、使用引用傳遞函式的引數,在記憶體中並沒有產生實參的副本,它是直接對實參操作;而使用一般變數傳遞函式的引數,當發生函式呼叫時,需要給形參分配儲存單元,形參變數是實參變數的副本;如果傳遞的是物件,還將呼叫拷貝建構函式。因此,當引數傳遞的資料較大時,用引用比用一般變數傳遞引數的效率和所佔空間都好 6、不能建立引用的陣列

作用:: 1、能夠修改呼叫函式中的資料物件 2、通過傳遞引用而不是整個資料物件,可以提高程式的執行速度

使用場景:: 對於傳遞使用的值而不做修改的函式: 1、如果資料物件很小,如內建資料型別或小型結構,則按值傳遞 2、如果資料物件為陣列,則使用指標,並將指標宣告為指向const的指標 3、如果資料物件是較大的結構,則使用const指標或者const引用,以提高程式的效率。這樣可以節省複製結構所需的時間和空間 4、如果資料物件是類物件,則使用const引用

對於修改呼叫函式中的資料的函式: 1、內建資料型別用指標 2、資料物件為陣列,則只能使用指標 3、資料物件為結構,使用引用或指標 4、資料物件為類物件,則使用引用

引用作為函式返回值:: 1、不能返回區域性變數的引用 2、不能返回函式內部new分配的記憶體的引用 3、可以返回類成員的引用,但最好是const

引用與多型:: 引用是除指標外另一個可以產生多型效果的手段。這意味著,一個基類的引用可以指向它的派生類例項

2.2:引用與指標的區別

★相同點: 都是地址的概念;指標指向一塊記憶體,它的內容是所指記憶體的地址;而引用則是某塊記憶體的別名。具體來說,指標是一個變數的地址,引用是一個變數的別名

★不同點: 1、指標是一個實體,而引用僅是個別名; 2、引用必須被初始化,指標不必 3、引用只能在定義時被初始化一次,之後不可變;指標可以改變所指的物件 4、引用不能為空,指標可以為空; 5、sizeof 引用得到的是所指向的變數(物件)的大小,而“sizeof 指標”得到的是指標本身的大小; 7、程式為指標變數分配記憶體區域,而引用不需要分配記憶體區域 8、指標和引用的自增(++)運算意義不一樣; 9、引用是型別安全的,而指標不是 (引用比指標多了型別檢查)

2.3:傳值與傳址

1、採用傳值呼叫方式時,在被呼叫函式中改變形參的值,只是改變其副本值,而不會影響呼叫函式中實參值 2、採用引用呼叫方式時,傳遞的是變數的地址值,這樣在被調函式中,對形參的操作實際上操作的是實參本身 3、陣列作為函式傳遞時,實際採用引用呼叫方式

2.4:變數a、指標*a、引用&a作為函式形參時的區別

#include <iostream>
using namespace std;
//變數作為形參
void swap1(int a, int b)
{
       a = a + b;
       b = a - b;
       a = a - b;
}
//指標作為形參
void swap2(int *a, int *b)
{
       *a = *a + *b;
       *b = *a - *b;
       *a = *a - *b;
}
//引用作為形參
void swap3(int &a, int &b)
{
       a = a + b;
       b = a - b;
       a = a - b;
}
int main()
{
       int a = 2;
       int b = 3;
       swap1(a, b); //普通變數時,不影響實參
       cout << a << ',' << b<< endl;//a = 2, b = 3
       swap2(&a, &b);//指標變數時,不影響指標本身,但通過指標可影響其指向的變數的值
       //要想修改指標本身,則函式形參為指標的指標**p
       cout << a << ',' << b << endl;//a = 3, b = 2
       swap3(a, b); //引用變數時,可修改實參值
       cout << a << ',' << b << endl; // a = 2, b = 3
}