天天看點

《好學的C++程式設計》——2.3 循址通路是怎樣的include

本節書摘來自異步社群出版社《好學的c++程式設計》一書中的第2章,第2.3節,作者: 張祖浩 , 沈天晴,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

好學的c++程式設計

變量存儲空間位址的指向

古詩雲:“借問酒家何處有?牧童遙指杏花村。”路人向牧童請問酒家的位址,牧童感到光口頭表述酒家位址不夠清楚,要用一個手指指向酒家,就清楚了。并且路人由此可知,隻要通路牧童所指,就能通路到所指杏花村酒樓的美酒佳肴了。

圖2.4中,“2012是變量a的存儲空間位址。”這個關系是用語言或文字來表述的。那麼,在圖形上如何來表述這種關系呢?受牧童啟發,簡單得很!從位址2012畫一個箭頭指向變量a就行了,如圖2.5所示。這種圖形讓人一眼就看明白:位址2012指向變量a。c++約定好:這表明2012是變量a的位址,這當然也就是變量a存儲空間的位址。

《好學的C++程式設計》——2.3 循址通路是怎樣的include

這樣,變量位址就有了指向,是哪個變量的位址,它就指向哪個變量。說到底,是哪個存儲空間的位址,它就指向哪個存儲空間。由此,指向存儲空間的位址就獲得了一個名稱,叫做“指針”。說白了,指針就是變量位址,就是變量存儲空間位址,實際就是變量存儲空間首位元組位址。這些位址不僅有指向,而且有了與所指變量存儲空間同樣的類型。

位址運算符 &

變量位址已如上所述。那麼,在程式中如何能從變量之名獲得該變量之位址呢?這就要靠位址運算符 &。在這裡,符号 & 作為位址運算符使用。

我們将變量名(例如a)置于位址運算符 & 的右側得 &a,則 &a就是變量a的位址。同理,&b就是變量b的位址。對圖2.4中情況而言,&a 就是位址2012,&b 就是位址2016。

要注意,& 用在不同的場合有不同的含義。在這裡,不是在聲明中,& 的右側是一個變量,這時,& 是作為位址運算符使用。不要和别名聲明中的 & 混淆。

指針變量

以上講的變量的位址,例如圖2.5中的位址2012,它是變量a位址,也就是變量a存儲空間的位址。這個位址之值還沒有存儲起來。如果要存儲,當然還要另配給一個存儲空間來存儲它。

若某變量(比如p),系統為其配給的存儲空間專門用來存放某另一變量(比如a)的位址,則此變量(p)就叫做指針變量,也可簡稱為指針。

上述位址2012就可用指針變量存儲空間進行存儲,隻要将該位址賦給該指針變量就行。

例如,圖2.6(a)中,右框表示某變量a的存儲空間,位址為 &a。具體說,&a就是2012。左框表示指針變量p的存儲空間,若将a的位址 &a作為指針值,賦給p,語句為:

則指針變量p的存儲空間中就存儲了變量a的位址 &a了,p的值就是變量a的位址2012。這時我們就說,變量p是變量a的指針,變量p指向了變量a。或者說,a是指針變量p所指的變量,如圖2.6(a)中箭頭所示。變量a和變量p之間的關系也可簡示如圖2.6(b)。

指針變量的值是可以變化的。如果指針p之值不是變量a的位址&a了,而改變為變量b的位址&b。則指針就不指向a,而改為指向b了。例如,看圖2.7(a),經指派語句p=&a;q=&b;使指針p指向了變量a,指針q指向了變量b。如果重作指派p=&b;q=&a;使兩個指針之值作了互換,則其指向也就互換了,如圖2.7(b)所示。

《好學的C++程式設計》——2.3 循址通路是怎樣的include

以上講的變量a、b都是int型變量,儲存其位址的變量p、q是int型指針變量。

指針變量的類型

我們知道,指針變量的值總是某個存儲空間位址。所指存儲空間是有類型的。我們把指針所指存儲空間的類型定義為指針變量的類型。這樣一來就可以知道,float型指針是指向float型存儲空間的;char型指針是指向char型存儲空間的。指針是什麼類型,所指存儲空間就是什麼類型。反過來說,所指存儲空間是什麼類型,指針就是什麼類型。反正指針(位址)的類型和所指存儲空間的類型是一緻的。

例如,int型變量i的位址 &i就是int型指針,char型變量c的位址&c就是char型指針。變量位址的類型和變量存儲空間的類型是一緻的,當然也和變量類型是一緻的。

總體來說,變量、變量存儲空間、存儲空間位址、存儲空間首位元組位址以及存放存儲空間位址的指針變量,都有類型,變量是什麼類型,它們就全都是什麼類型。它們的類型和變量類型完全一緻。

單純的一個記憶體單元位址,它不指向某個變量(或存儲空間)談不上什麼類型。沒有類型的指針叫void型指針(如以vp表示)。任何其他類型的指針都可直接對vp進行指派,指派隻是将一個位址值賦給了它,它仍然是void型指針。反過來,若void型指針vp想對别的指針進行指派,則必須先将vp進行強迫類型轉換為相同類型後,然後才可進行指派。

指針變量初次出場也要作聲明,表明指針變量屬何類型。

指針變量聲明過後,系統就配給它存儲空間,用以存儲指針變量之值。不管指針是何類型,指針值總是一個8位十六進制的位址值。用4個位元組大小的存儲空間存儲位址就行。

但指針所指存儲空間的大小,則随指針類型的不同而有所不同。比如,char型指針所指char型存儲空間大小為1個位元組,而double型指針所指double型存儲空間大小則為8個位元組(見表2-1)。

對于圖2.7中的變量a和指針變量p,假設變量a是int類型,可作如下聲明:

第一條語句是對int型變量a的聲明,大家是熟悉的。

第二條語句是對指針變量p作的聲明。在這個聲明中,“int”和“*”一起作為一個整體,描述了p是一個int型指針變量。接着的“=”是用int型變量a的位址&a,給p初始化。記住!記住:誰的位址給指針變量初始化,指針變量就指向誰。這樣,指針p的初值就是&a,指針p指向了變量a,如圖2.8所示。

《好學的C++程式設計》——2.3 循址通路是怎樣的include

當然,不在聲明中進行初始化,而在聲明過後另寫一個指派語句也是可以的。例如:

頭兩句對變量a和指針變量p作了聲明。末句對指針p賦以 &a,使p指向了a。記住!記住:誰的位址給指針變量初始化(或指派),指針變量就指向誰。

上述3條語句合并寫成如下一條語句也行:

這裡,雖然“int”和“”不緊靠在一起,但仍在同一個聲明語句中,它倆仍然作為一個整體,描述了p是一個int型指針變量。接着的“=”是對p進行初始化,将變量a的位址 &a作為指針變量p的初值。這裡,如果脫離整體考慮,而認為把 &a 賦給了 p ,那就錯啦。

上述聲明過後,我們可以稱p為“int型指針變量”,也可以稱p為“int 型變量”。其中“int ”作為一個整體,表明了變量p是一個int 型指針。

指針聲明的一般形式為:

同類型的指針,互相間可以指派,例如:

a行語句中,聲明了兩個int型指針變量p和q。b行語句将變量a的位址&a賦給了指針變量p,使p指向了a。c行語句對同類型指針變量q賦以p值,使q值也等于a的位址,因而也指向了a。p和q都指向同一個變量a,如圖2.9所示。

《好學的C++程式設計》——2.3 循址通路是怎樣的include

void型指針vp的聲明語句形式是:void *vp;

要注意,“”用在不同的場合有不同的含義。在這裡,“”用在變量的聲明中,它表示這是一個指針變量。

我們知道,所謂對變量進行通路,就是對變量的存儲空間進行通路,就是對變量的存儲空間進行寫值或讀值。

與直呼其名的通路不同。用指針進行通路是循址通路。循址通路不涉及變量之名,而是對指針(位址)所指存儲空間的内容進行通路。

那麼,怎樣才能由指針來獲得指針所指存儲空間的内容呢?這就要靠所指運算符 *。

所指運算符 *

在這裡,運算符 作為所指運算符使用。我們将指針名(比如p)置于所指運算符 的右側,得 p,則p就是指針p所指存儲空間的内容。同理,指針q所指存儲空間内容就是 *q。

要注意,“”用在不同的場合有不同的含義。在這裡,不是在聲明中,“”的右側是一個指針p,這p就表示指針p所指存儲空間的内容。“”作為所指運算符使用。

指針所指和所指變量

對于指針所指存儲空間的内容,本書簡稱之為指針所指。例如p就是指針p所指存儲空間的内容,簡稱 p為指針p的所指。

如果将變量a的位址 &a賦給指針p,即:

則p就指向了變量a,這時,p所指存儲空間的内容就是變量a。指針所指*p和所指變量a二者同是p所指存儲空間的内容,二者是一回事,故得下列等價式:

公式3

由此可得結論:如果指針p指向變量a,則通路指針所指 *p,就是通路所指變量a。記住!記住!

快餐循址派送

張三想吃快餐,隻要告知張三家住宅位址。大堂經理就啟動送餐程式,送餐員就出馬給位址所指賦送快餐。送餐程式之内沒有客戶尊姓大名,送餐員隻管埋頭按位址所指送餐。給位址所指的住宅空間賦送了快餐,實際就是給程式之外的張三賦送了快餐。真是:

這裡的關鍵就在于,必須将張三住宅位址賦入送餐員本子,使送餐員本子中的位址(指針)指向張三。這樣,就能實作通路指針所指就是通路所指張三。給指針所指送餐就是給所指張三送餐。

用指針所指對所指變量進行通路例

【例2-3】參照【例2-2】,說明通路指針所指就是通路所指變量。程式如下:

程式運作結果為:

此例不妨與【例2-2】對照來看。程式中,a行聲明了3個int型變量a、b和c,并都有了初值。b行輸出a、b和c,輸出結果如f行所示。

在c行聲明了3個int型指針變量p1、p2和p3,并且分别用變量a、b和c的位址給予初始化。這樣,就使指針p1、p2和p3分别指向了變量a、b和c。

在d行的一對花括弧内,對指針所指 p1、p2和 *p3都分别作了增值指派,即分别取三者的原值加上一定的值後,再重新分别賦給三者。

e行又輸出a、b和c,輸出結果如g行所示。

奇怪!從f和g行的輸出結果相比來看,a、b和c的值都變大了。可是,從整個程式來看,并未對a、b和c進行增值指派呀!何以會變大呢?

關鍵在于c行的語句,用a、b和c的位址分别對3個指針p1、p2和p3進行了初始化。誰的位址給指針變量初始化(或指派),指針變量就指向誰。這就使3個指針分别指向了變量a、b和c。記得嗎?如果指針指向了某變量,則通路指針所指就是通路所指變量。在d行,雖然是在花括弧内,對指針所指p1、p2和*p3進行增值指派,但是實際就是在花括弧外,對所指變量a、b和c進行增值指派。具體來說,就是有如下的關系:

以後會看到,d行花括弧内的程式可以讓一個函數來完成。到那時,可以看到如下情景:

這恰似按送餐規程内的程式對指針所指進行送餐,實際就是規程之外的張三獲得快餐。在這裡,先提前讓大家領會一下,指針在後面内容中将有精彩表現。

某指針指向某變量的兩個等價式

綜上所述可知,如果在圖形中出現指針的指向關系:t圖檔 14h。則可獲得下列兩個等價式,這兩個等價式在後續内容中常用到:

(1)t圖檔 15&h; (2)*t圖檔 16h。

(1)式表明t的值就是變量h的位址,指針t指向了變量h;

(2)式表明指針所指t就是所指變量h。通路指針所指t就是通路所指變量h。

随機值指針是危險分子

若聲明了一個指針變量p,并未對它進行初始化或指派,則p值(位址值)将是一個随機數,這樣的指針叫做随機值指針。随機值指針說不定是指向哪個存儲空間。所指存儲空間的内容也說不定是什麼内容。若所指存儲空間内容是很重要的資料,這時冒然給指針所指 *p指派,就将使該重要資料丢失,可能造成重大故障。是以說,随機值指針是一個危險分子。

若一時不知用哪個變量的位址對指針進行初始化或指派為好,這時可暫時先用一個空值(null或0)進行初始化或指派,以代替指針存儲空間裡的随機數。例如:

或:<code>`</code>javascript

int *p=0;

double a;

double *u=&amp;a;

double **x=&amp;u;

double *u;

double **x;

u=&amp;a;

x=&amp;u;

double a, u=&amp;a, *x=&amp;u;

資料類型 **變量名;

using namespace std;

int main()

{

  char a='a',b='b',t;           //a

  char u=&amp;a, v=&amp;b;           //b

  char x=&amp;u, y=&amp;v;          //c

a=a b=b                //g

a=b b=a               //h

繼續閱讀