1. 程式人生 > 實用技巧 >C++指標(pointer)

C++指標(pointer)

C++指標(pointer)

在電腦科學中,指標(Pointer),是程式語言中的一類資料型別及其物件或變數,用來表示或儲存一個儲存器地址,這個地址的值直接指向(points to)存在該地址的物件的值。

指標的概念

指標(pointer)是一個特殊的變數,它裡面儲存的數值被解釋為記憶體裡的一個地址。

指標是作業系統記憶體的重要途徑。指標的值是指標本身儲存的數值,這個值將被編譯器當作一個地址,而不是一個一般的數值。指標所指向的記憶體區就是從指標的值所代表的那個記憶體地址開始的,長度為sizeof(指標所指向的型別)的一片記憶體區。以後,我們說一個指標的值是XX,就相當於說該指標指向了以XX為首地址的一片記憶體區域;我們說一個指標指向了某塊記憶體區域,就相當於說該指標的值是這塊記憶體區域的首地址。

指標變數本身所佔據的記憶體區的大小,可用函式sizeof(指標的型別)測得。

取地址運算子& :作用於記憶體中一個可定址的資料(如變數、物件和陣列元素等等),操作的結果是獲得該資料的地址。

間接定址運算子* :作用於一個指標型別的變數,訪問該指標所指向的變數。也稱為取內容運算子,或間接引用運算子。

如果一個變數宣告時在前面使用 * 號,表明這是個指標型變數。換句話說,該變數儲存一個地址,而 *(此處特指單目運算子*,下同;C++語言中另有雙目運算子 * 表示乘) 則是取內容操作符,意思是取這個記憶體地址裡儲存的內容。把這兩點結合在一起,可將 int *a;看作是 “*a 解得的內容型別為 int”。指標是 C++語言區別於其他同時代高階語言的主要特徵之一。

指標不僅可以是變數的地址,還可以是陣列、陣列元素、函式的地址。通過指標作為形式引數可以在函式的呼叫過程得到一個以上的返回值(不同於return z這樣的僅能得到一個返回值。

例:

int *pi; // 指向整型資料的指標pi

int * api[3]; // 由指向整型資料的指標構成的陣列,長度為 3

char ** argv; // 指向一個字元指標的指標

struct { int member; } stinst,

* pst = & stinst; // pst是一個指向一個匿名結構體的指標

儲存在指標中的地址所指向的數值在程式中可以由 * 讀取。例如,在第一個例子中, *pi 是一個整型資料。這叫做引用一個指標。

另一個運算子 &,叫做取地址運算子,它將返回一個變數、陣列或函式的儲存地址。因此,下面的例子:

int i, *pi; /* int and pointer to int */

pi = &i;

i 和 *pi 在程式中可以相互替換使用,直到 pi 被改變成指向另一個變數的地址。

當指標指向結構體時,可以使用運算子 -> 代替 *和. 的作用,如 (*p).m 與 p->m 等效。

指標的使用一定要特別小心,千萬不能越界,否則會出現意想不到的結果。而且一定不要使用未初始化過的指標(野指標)。

要想深入理解指標,應從回顧變數的定義開始入手,變數的定義,是向系統申請一塊適當大小的記憶體,來存放對應的資料,比如

int a = 100;

char c = 'x';

定義變數時,系統為變數分配相應的儲存單元,通過變數名可以直接使用該儲存單元。

對程式設計開發初學者而言,充分理解以上變數a、b的記憶體地址,可以有助於理解指標。

先簡要介紹如何檢視變數在記憶體中的地址和內容。

先看C語言檢視變數在記憶體中的地址和內容

#include<stdio.h>

int main()

{

int i = 0;

int *p=&i;

/* %p 十六進位制,大寫,高位零顯示

%x 十六進位制,小寫,高位0不顯示 */

printf("變數的地址: %p\n", &i);

printf("變數的地址: %x\n", &i);

printf("變數的值為:%d\n",i);

printf("指標(地址)的值為:%p\n",p);

printf("指標(地址)的值為:%x\n",p);

return 0;

}

執行之,參見下圖:

再看C++語言檢視變數在記憶體中的地址和內容

#include <iostream>

using namespace std;

int main ()

{

int i = 0;

int *p=&i;

/* 在c++裡面怎麼列印變數的地址

普通變數 int i; cout<<&i;

指標(地址)的值 int *p=&i; cout<< p

陣列變數 int a[20]; cout<<(void *)a; */

cout<<"變數的地址:" << &i <<endl;

cout<<"指標的地址:" << p <<endl;

cout<<"變數的值為:" << i <<endl;

int b[20];

cout<<"陣列變數的地址:" << (void *)b<<endl;

return 0;

}

執行之,參見下圖:

指標變數本身所佔據的記憶體區的大小,可用函式sizeof(指標的型別)測得。作業系統的位數決定了指標變數所佔的位元組數。一個指標變數不管它是指向任何型別(如整型、還是字元型、雙精度型)變數,對於32位系統,指標變數本身只佔4個位元組,對於64位系統,指標變數本身只佔8個位元組。

測試指標變數佔有記憶體空間大小的例子:

#include <iostream>

using namespace std;

int main ()

{

char s = 'a', *p1 =&s;

cout << "指標的大小" << sizeof(p1) << endl;

int a = 10, *p2 = &a;

cout << "指標的大小" << sizeof(p2) << endl;

return 0;

}

執行之,參見下圖:

我們已經知道,儲存單元的使用可以通過變數名,定義變數時,系統為變數分配相應的儲存單元,通過變數名可以直接使用該儲存單元。儲存單元的使用還可以地址來使用。通過儲存單元的地址來使用該儲存單元,這就需要用到指標變數。

存貯變數的記憶體空間的首地址稱為該變數的地址。由於指標變數中的值是另一個變數的地址,我們習慣上形象地稱為指標變數指向該變數。

指標變數定義的一般形式為:

型別說明符 *變數名;

其中,*表示這是一個指標變數,變數名即為定義的指標變數名,型別說明符表示本指標變數所指向的變數的資料型別。例

int *p = &i;

指標變數p的前面有一個星號,這個星號被稱為指標變數定義標記,指標變數讓我們有另一種渠道來操作記憶體塊:

i = 100;

*p = 100;

以上兩行程式碼,效果是完全一致的。也就是說,*p就是i,此處的星號被稱為間接定址運算子,旨在令p指向的目標i。

指標變數的賦值

指標變數同普通變數一樣,使用之前不僅要定義說明, 而且必須賦予具體的值。未經賦值的指標變數不能使用, 否則將造成系統混亂,甚至宕機。指標變數的賦值只能賦予地址,決不能賦予任何其它資料,否則將引起錯誤。在C++語言中,變數的地址是由編譯系統分配的,對使用者完全透明,使用者不知道變數的具體地址。C++語言中提供了地址運算子&來表示變數的地址。其一般形式為:

&變數名;如:&i變示變數i的地址,&b表示變數b的地址。 變數本身必須預先說明。

假設有指向整型變數的指標變數p,如要把整型變數i 的地址賦予p可以有以下兩種方式:

(1)指標變數初始化的方法

int i;

int *p = &i;

(2)賦值語句的方法

int i;

int *p;

p = &i;

再次提醒注意:

1)不允許把一個數賦予指標變數,故下面的賦值是錯誤的:

int *p;

p = 100;

因為指標變數的賦值只能賦予地址。

2)賦值時指標變數前不能再加“*”說明符,如:

*p = &i; //是錯誤的

int int I = 10, *p = &i; //指標變數初始化時可以,是正確的

指標賦值有三種情況 :

1)取變數地址:使指標指向該變數。

int y=15;

int *ip;

ip=&y

參見下圖:

2)指標相互賦值:使兩指標指向同一變數。

(續上)

int *ip1;

ip1=ip;

參見下圖:

3)指標賦NULL:空指標,指標懸空。不同於指標未賦值。

int *ip2 = NULL; // ip2的值是 0

下面給出一個使用取地址運算子&和取內容運算子(間接定址運算子)*的簡單例子

#include <iostream>

using namespace std;

int main ()

{

int i=10,*p=&i;

cout<<"i= " << i <<endl;

cout<<"*p= " << *p <<endl;

int a=5,*q;

q=&a;

cout<<"a= " << a <<endl;

cout<<"*q= " << *q <<endl;

return 0;

}

執行之,參見下圖:

取內容運算子(間接定址運算子)的說明

使用指標變數時,*號表示 操作 指標所指向的記憶體空間中的值。

假設有:

int i = 10;

*p相當於通過地址(p變數的值)找到一塊記憶體;然後操作記憶體。

*p放在等號的左邊賦值(給記憶體賦值),如:

int *p = &i;

*p放在等號的右邊取值(從記憶體獲取值),如:

int *q = *P;

取內容運算子(間接定址運算子)的使用例子:

#include <iostream>

using namespace std;

int main ()

{

int i = 10, *p;

p = &i; //p指向i的記憶體地址

cout<<"*p= " << *p <<endl;

cout<<"p= " << p <<endl;

int *q = &i;

*q= *q+4; //等號的左邊*q是賦值——給記憶體賦值;等號的右邊*q是取值——從記憶體獲取值。

cout<<"*q= " << *q <<endl;

int j;

j = *p + 5;

cout<<"*p= " << *p <<endl;

cout<<"j= " << j <<endl;

return 0;

}

執行之,參見下圖:

指標使用