3.3 指針 (Pointers)我們已經明白變量其實是可以由標識來存取的內存單元。但這些變量實際上是存儲在內存中具體的位置上的。對我們的程序來說,計算機內存只是一串連續(xù)的單字節(jié)單元(1byte cell),即最小數(shù)據單位,每一個單元有一個唯一地址。 計算機內存就好像城市中的街道。在一條街上,所有的房子被順序編號,每所房子有唯一編號。因此如果我們說芝麻街27號,我們很容易找到它,因為只有一所房子會是這個編號,而且我們知道它會在26號和28號之間。 同房屋按街道地址編號一樣,操作系統(tǒng)(operating system)也按照唯一順序編號來組織內存。因此,當我們說內存中的位置1776,我們知道內存中只有一個位置是這個地址,而且它在地址1775和1777之間。
地址操作符/去引操作符 Address/dereference operator (&)當我們聲明一個變量的同時,它必須被存儲到內存中一個具體的單元中。通常我們并不會指定變量被存儲到哪個具體的單元中—幸虧這通常是由編譯器和操作系統(tǒng)自動完成的,但一旦操作系統(tǒng)指定了一個地址,有些時候我們可能會想知道變量被存儲在哪里了。 這可以通過在變量標識前面加與符號ampersand sign (&)來實現(xiàn),它表示"...的地址" ("address of"),因此稱為地址操作符(adress operator),又稱去引操作符(dereference operator)。例如:
將變量andy的地址賦給變量ted,因為當在變量名稱andy 前面加ampersand (&) 符號,我們指的將不再是該變量的內容,而是它在內存中的地址。 假設andy 被放在了內存中地址1776的單元中,然后我們有下列代碼: andy = 25;
fred = andy; ted = &andy; 其結果顯示在下面的圖片中: 我們將變量andy 的值賦給變量fred,這與以前我們看到很多例子都相同,但對于ted,我們把操作系統(tǒng)存儲andy的內存地址賦給它,我們想像該地址為1776 (它可以是任何地址,這里只是一個假設的地址),原因是當給ted 賦值的時候,我們在andy 前面加了ampersand (&) 符號。 存儲其它變量地址的變量(如上面例子中的ted ),我們稱之為指針(pointer)。在C++ 中,指針pointers 有其特定的優(yōu)點,因此經常被使用。在后面我們將會看到這種變量如何被聲明。
引用操作符Reference operator (*)使用指針的時候,我們可以通過在指針標識的前面加星號asterisk (*)來存儲該指針指向的變量所存儲的數(shù)值,它可以被翻譯為“所指向的數(shù)值”("value pointed by")。因此,仍用前面例子中的數(shù)值,如果我們寫: beth = *ted; (我們可以讀作:"beth 等與ted所指向的數(shù)值") beth 將會獲得數(shù)值25,因為ted 是1776,而1776 所指向的數(shù)值為25。 你必須清楚的區(qū)分ted 存儲的是1776,但*ted (前面加asterisk * ) 指的是地址1776中存儲的數(shù)值,即25。注意加或不加星號*的不同(下面代碼中注釋顯示了如何讀這兩個不同的表達式): beth = ted; // beth 等于 ted ( 1776
)
beth = *ted; // beth 等于 ted 所指向的數(shù)值 ( 25 )
地址或反引用操作符Operator of address or dereference (&) 它被用作一個變量前綴,可以被翻譯為“…的地址”("address of"),因此:&variable1 可以被讀作 variable1的地址("address of variable1" )。 引用操作符Operator of reference (*) 它表示要取的是表達式所表示的地址指向的內容。它可以被翻譯為“…指向的數(shù)值” ("value pointed by")。 * mypointer 可以被讀作 "mypointer指向的數(shù)值"。 繼續(xù)使用上面開始的例子,看下面的代碼: andy = 25;
ted = &andy; 現(xiàn)在你應該可以清楚的看到以下等式全部成立: andy == 25
&andy == 1776 ted == 1776 *ted == 25 第一個表達式很容易理解,因為我們有賦值語句andy=25;。第二個表達式使用了地址(或反引用)操作符(&) 來返回變量andy的地址,即 1776。第三個表達式很明顯成立,因為第二個表達式為真,而我們給ted賦值的語句為ted = &andy;。第四個表達式使用了引用操作符 (*),相當于ted指向的地址中存儲的數(shù)值,即25。 由此你也可以推斷出,只要ted 所指向的地址中存儲的數(shù)值不變,以下表達式也為真:
聲明指針型變量Declaring variables of type pointer由于指針可以直接引用它所指向的數(shù)值,因此有必要在聲明指針的時候指明它所指向的數(shù)據類型。指向一個整型int或浮點型float數(shù)據的指針與指向一個字符型char數(shù)據的指針并不相同。 因此,聲明指針的格式如下:
這里,type 是指針所指向的數(shù)據的類型,而不是指針自己的類型。例如: int * number;
char * character; float * greatnumber; 它們是3個指針的聲明,每一個指針指向一種不同數(shù)據類型。這三個指針本身其實在內存中占用同樣大小的內存空間(指針的大小取決于不同的操作系統(tǒng)),但它們所指向的數(shù)據是不同的類型,并占用不同大小的內存空間,一個是整型int,一個是字符型char ,還有一個是浮點型float。 需要強調的一點是,在指針聲明時的星號asterisk (*) 僅表示這里聲明的是一個指針,不要把它和前面我們用過的引用操作符混淆,雖然那也是寫成一個星號 (*)。它們只是用同一符號表示的兩個不同任務。
注意變量value1 和 value2 是怎樣間接的被改變數(shù)值的。首先我們使用 ampersand sign (&) 將value1的地址賦給mypointer 。然后我們將10 賦給 mypointer所指向的數(shù)值,它其實指向value1的地址,因此,我們間接的修改了value1的數(shù)值。 為了讓你了解在同一個程序中一個指針可以被用作不同的數(shù)值,我們在這個程序中用value2 和同一個指針重復了上面的過程。 下面是一個更復雜一些的例子:
上面每一行都有注釋說明代碼的意思:ampersand (&) 為"address of",asterisk (*) 為 "value pointed by"。注意有些包含p1 和p2 的表達式不帶星號。加不加星號的含義十分不同:星號(*)后面跟指針名稱表示指針所指向的地方,而指針名稱不加星號(*)表示指針本身的數(shù)值,即它所指向的地方的地址。 另一個需要注意的地方是這一行:
聲明了上例用到的兩個指針,每個帶一個星號(*),因為是這一行定義的所有指針都是整型int (而不是 int*)。原因是引用操作符(*) 的優(yōu)先級順序與類型聲明的相同,因此,由于它們都是向右結合的操作,星號被優(yōu)先計算。我們在 section 1.3: Operators 中已經討論過這些。注意在聲明每一個指針的時候前面加上星號asterisk (*)。
指針和數(shù)組Pointers and arrays數(shù)組的概念與指針的概念聯(lián)系非常解密。其實數(shù)組的標識相當于它的第一個元素的地址,就像一個指針相當于它所指向的第一個元素的地址,因此其實它們是同一個東西。例如,假設我們有以下聲明: int numbers [20];
int * p; 下面的賦值為合法的:
這里指針p 和numbers 是等價的,它們有相同的屬性,唯一的不同是我們可以給指針p賦其它的數(shù)值,而numbers 總是指向被定義的20個整數(shù)組中的第一個。所以,p只是一個普通的指針變量,而與之不同,numbers 是一個指針常量(constant pointer),數(shù)組名的確是一個指針常量。因此雖然前面的賦值表達式是合法的,但下面的不是:
因為numbers 是一個數(shù)組(指針常量),常量標識不可以被賦其它數(shù)值。 由于變量的特性,以下例子中所有包含指針的表達式都是合法的:
在數(shù)組一章中我們使用了括號[]來指明我們要引用的數(shù)組元素的索引(index)。中括號[]也叫位移(offset)操作符,它相當于在指針中的地址上加上括號中的數(shù)字。例如,下面兩個表達式互相等價: a[5] = 0; // a [offset of 5] =
0
*(a+5) = 0; // pointed by (a+5) = 0 不管a 是一個指針還是一個數(shù)組名, 這兩個表達式都是合法的。
指針初始化Pointer initialization當聲明一個指針的時候我們可能需要同時指定它們指向哪個變量, int number;
int *tommy = &number; 這相當于: int number;
int *tommy; tommy = &number; 當給一個指針賦值的時候,我們總是賦給它一個地址值,而不是它所指向數(shù)據的值。你必須考慮到在聲明一個指針的時候,星號 (*) 只是用來指明它是指針,而從不表示引用操作符reference operator (*)。記住,它們是兩種不同操作,雖然它們寫成同樣的符號。因此,我們要注意不要將以上的代碼與下面的代碼混淆: int number;
int *tommy; *tommy = &number; 這些代碼也沒有什么實際意義。 在定義數(shù)組指針的時候,編譯器允許我們在聲明變量指針的同時對數(shù)組進行初始化,初始化的內容需要是常量,例如:
在這個例子中,內存中預留了存儲"hello" 的空間,并且terry被賦予了向這個內存塊的第一個字符(對應’h’)的指針。假設"hello"存儲在地址1702,下圖顯示了上面的定義在內存中狀態(tài): 這里需要強調,terry 存儲的是數(shù)值1702 ,而不是'h' 或 "hello",雖然1702 指向這些字符。 指針terry 指向一個字符串,可以被當作數(shù)組一樣使用(數(shù)組只是一個常量指針)。例如,如果我們的心情變了,而想把terry指向的內容中的字符'o' 變?yōu)榉?!' ,我們可以用以下兩種方式的任何一種來實現(xiàn): terry[4] = '!';
*(terry+4) = '!'; 記住寫 terry[4] 與*(terry+4)是一樣的,雖然第一種表達方式更常用一些。以上兩個表達式都會實現(xiàn)以下改變: 指針的數(shù)學運算Arithmetic of pointers對指針進行數(shù)學運算與其他整型數(shù)據類型進行數(shù)學運算稍有不同。首先,對指針只有加法和減法運算,其它運算在指針世界里沒有意義。但是指針的加法和減法的具體運算根據它所指向的數(shù)據的類型的大小的不同而有所不同。 我們知道不同的數(shù)據類型在內存中占用的存儲空間是不一樣的。例如,對于整型數(shù)據,字符char 占用1 的字節(jié)(1 byte),短整型short 占用2 個字節(jié),長整型long 占用4個字節(jié)。 假設我們有3個指針: char *mychar;
short *myshort; long *mylong; 而且我們知道他們分別指向內存地址1000, 2000 和3000。 因此如果我們有以下代碼: mychar++;
myshort++; mylong++; 就像你可能想到的,mychar的值將會變?yōu)?001。而myshort 的值將會變?yōu)?002,mylong的值將會變?yōu)?004。原因是當我們給指針加1時,我們實際是讓該指針指向下一個與它被定義的數(shù)據類型的相同的元素。因此,它所指向的數(shù)據類型的長度字節(jié)數(shù)將會被加到指針的數(shù)值上。以上過程可以由下圖表示: 這一點對指針的加法和減法運算都適用。如果我們寫以下代碼,它們與上面例子的作用一抹一樣: mychar = mychar + 1; myshort = myshort + 1;
mylong = mylong + 1; 這里需要提醒你的是,遞增 (++) 和遞減 (--) 操作符比引用操作符reference operator (*)有更高的優(yōu)先級,因此,以下的表達式有可能引起歧義: *p++;
*p++ = *q++; 第一個表達式等同于*(p++) ,它所作的是增加p (它所指向的地址,而不是它存儲的數(shù)值)。 在第二個表達式中,因為兩個遞增操作(++) 都是在整個表達式被計算之后進行而不是在之前,所以*q 的值首先被賦予*p ,然后q 和p 都增加1。它相當于: *p = *q;
p++; q++; 像通常一樣,我們建議使用括號()以避免意想不到的結果。
指針的指針Pointers to pointersC++ 允許使用指向指針的指針。要做到這一點,我們只需要在每一層引用之前加星號(*)即可: char a;
char * b; char ** c; a = 'z'; b = &a; c = &b; 假設隨機選擇內存地址為7230, 8092 和10502,以上例子可以用下圖表示: (方框內為變量的內容;方框下面為內存地址) 這個例子中新的元素是變量c,關于它我們可以從3個方面來討論,每一個方面對應了不同的數(shù)值: c 是一個(char **)類型的變量,它的值是8092 *c 是一個(char*)類型的變量,它的值是7230 **c 是一個(char)類型的變量,它的值是'z'
空指針void pointers指針void 是一種特殊類型的指針。void 指針可以指向任意類型的數(shù)據,可以是整數(shù),浮點數(shù)甚至字符串。唯一個限制是被指向的數(shù)值不可以被直接引用(不可以對它們使用引用星號*),因為它的長度是不定的,因此,必須使用類型轉換操作或賦值操作來把void 指針指向一個具體的數(shù)據類型。 它的應用之一是被用來給函數(shù)傳遞通用參數(shù):
sizeof 是C++的一個操作符,用來返回其參數(shù)的長度字節(jié)數(shù)常量。例如,sizeof(char) 返回 1,因為 char 類型是1字節(jié)長數(shù)據類型。
函數(shù)指針Pointers to functionsC++ 允許對指向函數(shù)的指針進行操作。它最大的作用是把一個函數(shù)作為參數(shù)傳遞給另外一個函數(shù)。聲明一個函數(shù)指針像聲明一個函數(shù)原型一樣,除了函數(shù)的名字需要被括在括號內并在前面加星號asterisk (*)。例如:
在這個例子里, minus 是一個全局指針,指向一個有兩個整型參數(shù)的函數(shù),它被賦值指向函數(shù)subtraction,所有這些由一行代碼實現(xiàn):
|
|