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