C和C++中最強(qiáng)大的功能莫過(guò)于指針了(pointer),,但是對(duì)于大多數(shù)人尤其是新手來(lái)說(shuō),,指針是一個(gè)最容易出錯(cuò)、也最難掌握的概念了,。本文將從指針的方方面面來(lái)講述指針的概念和用法,希望對(duì)大家有所幫助,。 內(nèi)存模型為了更好地理解指針,,讓我們來(lái)看一下計(jì)算機(jī)的內(nèi)存模型,。 內(nèi)存分為物理內(nèi)存和虛擬內(nèi)存,物理內(nèi)存對(duì)應(yīng)計(jì)算機(jī)中的內(nèi)存條,,虛擬內(nèi)存是操作系統(tǒng)內(nèi)存管理系統(tǒng)假象出來(lái)的,。由于這些不是我們本文的重點(diǎn),,下面不做區(qū)分,。有不清楚這些概念的同學(xué),,可以給我留言或者在線詢(xún)問(wèn)。 在不考慮cpu緩存的情況下,,計(jì)算機(jī)運(yùn)行程序本質(zhì)上就是對(duì)內(nèi)存中的數(shù)據(jù)的操作,,通俗地來(lái)說(shuō),,就是將內(nèi)存條某些部分的數(shù)據(jù)搬進(jìn)搬出或者搬來(lái)搬去,其中“搬進(jìn)搬出”是指將內(nèi)存中的二進(jìn)制數(shù)據(jù)搬入cpu寄存器及運(yùn)算器中進(jìn)行相應(yīng)的加減運(yùn)算或者將寄存器中的數(shù)據(jù)搬回內(nèi)存單元中,,而“搬來(lái)搬去”是指將內(nèi)存中的數(shù)據(jù)由這個(gè)位置搬到另外一個(gè)位置(當(dāng)然,一般不是直接搬,,而是借助寄存器作為中間存儲(chǔ)區(qū)),。如下圖所示: 計(jì)算機(jī)為了方便管理內(nèi)存,,將內(nèi)存的每個(gè)單元用一個(gè)數(shù)字編號(hào),如下圖所以: 圖中所示,,是一個(gè)大小為128個(gè)字節(jié)的內(nèi)存空間,其中每一個(gè)空格代表一個(gè)字節(jié),,所以?xún)?nèi)存編號(hào)是0~127,。 對(duì)于一個(gè)32位的操作系統(tǒng)來(lái)說(shuō),內(nèi)存空間中每一個(gè)字節(jié)的編號(hào)是一個(gè)32位二進(jìn)制數(shù),,所以?xún)?nèi)存編號(hào)從0000 0000 0000 0000 0000 0000 0000 0000至1111 1111 1111 1111 1111 1111 1111 1111,,轉(zhuǎn)換成16進(jìn)制也就是0x00000000至0xFFFFFFFF,由于是從0開(kāi)始的,,所以化成10機(jī)制就是從0至2的32次方減1,;對(duì)于64位操作系統(tǒng),內(nèi)存編號(hào)也就是從64個(gè)0至64個(gè)1,。 大家需要注意的是,從上面兩個(gè)圖我們可以發(fā)現(xiàn),,我們一般將編號(hào)小的內(nèi)存單元畫(huà)在上面,,編號(hào)大的畫(huà)在下面,也就是說(shuō)從上至下,,內(nèi)存編號(hào)越來(lái)越大,。
指針與指針變量指針的本意就是內(nèi)存地址,我們可以通俗地理解成內(nèi)存編號(hào),,既然計(jì)算機(jī)通過(guò)編號(hào)來(lái)操作內(nèi)存單元,,這就造就了指針的高效率。 那么什么是指針變量呢,?指針變量可通俗地理解成存儲(chǔ)指針的變量,,也就是存儲(chǔ)內(nèi)存地址(內(nèi)存編號(hào))的變量。首先指針變量和整型變量,、字符型變量以及其他數(shù)據(jù)類(lèi)型的變量一樣都是變量類(lèi)型,;但是,反過(guò)來(lái),,我們不應(yīng)該按這樣的方式來(lái)分類(lèi),,即:整型指針變量、字符型指針變量,、浮點(diǎn)型指針變量等等,。為什么不推薦這樣的分類(lèi)方法呢?首先,,指針變量就是一個(gè)數(shù)據(jù)類(lèi)型,,指針數(shù)據(jù)類(lèi)型,這種數(shù)據(jù)類(lèi)型首先是一個(gè)變量數(shù)據(jù)類(lèi)型,,那么它的大小是多少呢,?很多同學(xué)理所當(dāng)然地認(rèn)為整型指針變量和一個(gè)字符指針變量的大小是不一樣的,這種認(rèn)識(shí)是錯(cuò)的,。指針變量也是一個(gè)變量,,它是一個(gè)用來(lái)存儲(chǔ)其他變量的內(nèi)存地址的,,更準(zhǔn)確地說(shuō),指針變量時(shí)用來(lái)存儲(chǔ)其他變量的內(nèi)存首地址的,,因?yàn)椴煌臄?shù)據(jù)類(lèi)型所占的內(nèi)存大小不一樣,。舉個(gè)例子,在32位機(jī)器上,,假如a是int型變量,,pa是指向a的指針變量,b是一個(gè)double型變量,,pb是指向b的指針變量,,那么a在內(nèi)存中占四個(gè)字節(jié),b在內(nèi)存中占8個(gè)字節(jié),,假如a在內(nèi)存中分布是從0x11111110~0x11111113,,而b在內(nèi)存中分布是0x11112221至0x11112228,那么指針變量pa中存儲(chǔ)的內(nèi)容是0x11111110,,而pb中存儲(chǔ)就是0x11112221,,看到了吧,也就是說(shuō),,pa和pb中存儲(chǔ)的都是地址,,而且都是32位的二進(jìn)制地址;再者,,因?yàn)榇鎯?chǔ)這樣的地址需要4個(gè)字節(jié),,所以無(wú)論是int型指針變量pa或者是double 型指針變量pb,它們所占的內(nèi)存大小都是四個(gè)字節(jié),,從這點(diǎn)來(lái)說(shuō),,不管什么類(lèi)型的指針都是一樣的,所以不論按整型指針變量,、字符型指針變量,、浮點(diǎn)型指針變量等等來(lái)區(qū)分指針變量。總結(jié)起來(lái),,指針變量和int,、float、char等類(lèi)型一樣同屬變量類(lèi)型,,指針變量類(lèi)型占四個(gè)字節(jié)(32位機(jī)器下),,存儲(chǔ)的是32位的內(nèi)存地址。下面的代碼證明這一點(diǎn):
上面介紹的是指針變量的一個(gè)方面,,指針變量還有另外一層含義:在C/C++中星號(hào)(*)被定義成取內(nèi)容符號(hào),,雖然所有指針變量占的內(nèi)存大小和存儲(chǔ)的內(nèi)存地址大小都是一樣的,但是由于存儲(chǔ)的只是數(shù)據(jù)的內(nèi)存首地址,,所以指針變量存儲(chǔ)的內(nèi)存地址所指向的數(shù)據(jù)類(lèi)型決定著如何解析這個(gè)首地址,,也就是說(shuō)對(duì)于int型指針變量,,我們需要從該指針變量存儲(chǔ)的(首)地址開(kāi)始向后一直搜索4個(gè)字節(jié)的內(nèi)存空間,以圖中的變量a為例就是從0x12ff60~0x12ff63,,對(duì)于變量b就是0x12ff44~0x12ff4b,。所以從這個(gè)意義來(lái)上講,當(dāng)我們使用*pa,,必須先知道pa是一個(gè)整型的指針,,這里強(qiáng)調(diào)“整型”,而a的值1也就存儲(chǔ)在從0x12ff60~0x12ff63這四個(gè)字節(jié)里面,,當(dāng)我們使用*pb,,必須先知道pb是一個(gè)double型指針,這里強(qiáng)調(diào)'double',也就是說(shuō)值2.0000存儲(chǔ)在0x12ff44~0x12ff4b這八個(gè)字節(jié)里面,。因此,,我們對(duì)指針變量進(jìn)行算術(shù)運(yùn)算,比如pa + 2,pb - -之類(lèi)的操作,,是以數(shù)據(jù)類(lèi)型大小為單位的,,也就是說(shuō)pa + 2,相當(dāng)于0x12ff60 + sizeof(int) * 2 = 0x12ff60 + 4 * 2 = 0x12ff68,不是0x12ff60 + 2哦,;而pb - -相當(dāng)于0x12ff44 + sizeof(double) * 1 = 0x12ff44 + 8 * 1 = 0x12ff4c,。理解這一點(diǎn)很重要。 同理&a + 2和&b - 1也是一樣(注意由于&b是一個(gè)指針常量,,所以寫(xiě)成&b - -是錯(cuò)誤的),。
指針變量和指針常量指針變量首先是一個(gè)變量,由于指針變量存儲(chǔ)了某個(gè)變量的內(nèi)存首地址,,我們通常認(rèn)為”指針變量指向了該變量“,,但是在這個(gè)時(shí)刻指針變量pa指向變量a,下個(gè)時(shí)候可能不存儲(chǔ)變量a的首地址,,而是存儲(chǔ)變量c的首地址,,那么我們可以認(rèn)為這個(gè)時(shí)候,pa不再指向a,,而是指向c,。請(qǐng)別嫌我啰嗦,為了幫助你理解,,我是故意說(shuō)得這么細(xì)的,,后面我們討論高級(jí)主題的時(shí)候,當(dāng)你覺(jué)得迷糊,,請(qǐng)回來(lái)反復(fù)咀嚼一下這段話,。也就是說(shuō)指針變量是一個(gè)變量,它的值可以變動(dòng)的,。 相反,,指針常量可通俗地理解為存儲(chǔ)固定的內(nèi)存單元地址編號(hào)的”量“,,它一旦存儲(chǔ)了某個(gè)內(nèi)存地址以后,不可再改存儲(chǔ)其他的內(nèi)存地址了,。所以指針常量是堅(jiān)韌,,因?yàn)樗币Фㄇ嗌讲环潘伞埃徽f(shuō)是”癡情“,,因?yàn)樗痹?jīng)滄海難為水“,。我這里講的指針常量對(duì)應(yīng)的是const關(guān)鍵字定義的量,而不是指針字面量,。像&a,&b,&a + 2等是指針字面量,,而const int *p = &a;中的p才算是真正的指針常量,指針常量一般用在函數(shù)的參數(shù)中,,表示該函數(shù)不可改變實(shí)參的內(nèi)容,。來(lái)看一個(gè)例子吧: 上面的函數(shù)由于修改了一個(gè)常指針(多數(shù)情況下等同指針常量),因而會(huì)編譯出錯(cuò):error C3892: “x”: 不能給常量賦值,。 指針變量與數(shù)組記得多年以前,,我在學(xué)生會(huì)給電子技術(shù)部和地理信息系統(tǒng)專(zhuān)業(yè)的同學(xué)進(jìn)行C語(yǔ)言培訓(xùn)時(shí),這是一個(gè)最讓他們頭疼和感到一頭霧水的話題,,尤其是指針變量與二維數(shù)組的結(jié)合,,我永遠(yuǎn)忘不了胡永月那一臉迷惑與無(wú)助的表情。今天我這里給大家深入地分析一下,。先看一個(gè)例子: 如果你能得出下面這樣的結(jié)果,,說(shuō)明你已經(jīng)基本上對(duì)數(shù)組與指針的概念理解清楚了: 通過(guò)上圖,我們可以知道*(a + 1) = 2, *(ptr - 1) = 5,。 且不說(shuō)很多同學(xué)根本得不到這樣的結(jié)果,,他們看到int *ptr = (int*)(&a+1);這樣的語(yǔ)句就已經(jīng)懵了,首先,,我們知道C語(yǔ)言中規(guī)定數(shù)組名表示這個(gè)數(shù)組的首地址,,而這里竟然出現(xiàn)了&a這樣的符號(hào),本來(lái)a就是一個(gè)指針常量了,,這里對(duì)&a再次取地址難道不是非法操作嗎,?哈哈,當(dāng)你有這樣的疑問(wèn)的時(shí)候,,說(shuō)明你對(duì)二維數(shù)組相關(guān)知識(shí)理解不深入,。我這里先給你補(bǔ)充下知識(shí)點(diǎn)吧: 看這樣一個(gè)二維數(shù)組:int arr[3][4],這個(gè)數(shù)組布局如下: 這是一個(gè)3行4列的數(shù)組,它在內(nèi)存中的分布如下: 這里每一個(gè)數(shù)組元素占4字節(jié)空間,,我們知道C語(yǔ)言規(guī)定,,數(shù)組名arr是整個(gè)數(shù)組元素的首地址,比如是0x0012ff08,而像arr[0]、arr[1],、arr[2]分別是數(shù)組第一行,、第二行、第三行的首地址,,也就是0x0012ff08,、0x0012ff18、0x0012ff28,。 我們把a(bǔ)rr,、arr[0]和&arr[0][0]單獨(dú)拿出來(lái)分析,因?yàn)閿?shù)組的首地址也是第一列的首地址,,同時(shí)也是第一個(gè)元素的首地址,,所以arr和arr[0]和&arr[0][0]表示的都是同一個(gè)地址,但是這三個(gè)首地址在進(jìn)行算術(shù)運(yùn)算時(shí)是有區(qū)別的,。如果&arr[0][0] + 1,,這里的相當(dāng)于跳一個(gè)元素的內(nèi)存字節(jié)數(shù),也就是4個(gè),;但是arr[0] + 1,,移動(dòng)的內(nèi)存字節(jié)數(shù)是一列元素所占的字節(jié)數(shù),也就是4 * 4 = 16個(gè),;最后,,也是最讓人迷惑的的就是arr + 1,這個(gè)時(shí)候移動(dòng)的內(nèi)存數(shù)目是整個(gè)數(shù)組占的內(nèi)存字節(jié)數(shù),也就是48個(gè)字節(jié)數(shù),,所以a + 1所表示的內(nèi)存地址已經(jīng)不屬于這個(gè)數(shù)組了,,這個(gè)地址位于數(shù)組最后一個(gè)元素所占內(nèi)存空間的下一個(gè)字節(jié)空間,。 光有這些知識(shí)還是不能解決上面的問(wèn)題,,我們?cè)傺a(bǔ)充一個(gè)知識(shí)點(diǎn)。 C++是一種強(qiáng)類(lèi)型的語(yǔ)言,,其中有一種類(lèi)型叫void類(lèi)型,,從本質(zhì)上說(shuō)void不是一種類(lèi)型,因?yàn)樽兞慷际恰庇蓄?lèi)型“的,,就好像人的性別,,不是男人就是女人,不存在無(wú)性別的人,,所以void更多是一種抽象,。在程序中,void類(lèi)型更多是用來(lái)”修飾“和”限制“一個(gè)函數(shù)的:例如一個(gè)函數(shù)如果不返回任何類(lèi)型的值,,可以用void作返回類(lèi)型,;如果一個(gè)函數(shù)無(wú)參數(shù)列表,可以用void作為參數(shù)列表,。 跟void類(lèi)型”修飾“作用不同,,void型指針作為指向抽象數(shù)據(jù)的指針,,它本質(zhì)上表示一段內(nèi)存塊。如果兩個(gè)指針類(lèi)型不同,,在進(jìn)行指針類(lèi)型賦值時(shí)必須進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換,,看下面的例子: 但是可以將任何指針類(lèi)型賦值給void類(lèi)型而無(wú)須進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換: 當(dāng)然,如果把void型指針轉(zhuǎn)換成并不是它實(shí)際指向的數(shù)據(jù)類(lèi)型,,其結(jié)果是不可預(yù)測(cè)的,。試想,如果把一個(gè)int型指針賦給void型,,然后再把這個(gè)void型指針強(qiáng)制轉(zhuǎn)換成double型指針,,這樣的結(jié)果是不可預(yù)測(cè)的。因?yàn)椴煌瑪?shù)據(jù)類(lèi)型所占內(nèi)存大小不一樣,,這樣做可能或截?cái)鄡?nèi)存數(shù)據(jù)或者會(huì)增加一些未知的額外數(shù)據(jù),。所以,最好是將void類(lèi)型指針轉(zhuǎn)換成它實(shí)際數(shù)據(jù)類(lèi)型指針,。 有了上面的說(shuō)明,,你應(yīng)該能看懂C函數(shù)庫(kù)中下面這個(gè)函數(shù)的簽名含義了吧? void *memcpy(void *dest,const void *src,size_t len); 在這里,,任何數(shù)據(jù)類(lèi)型的指針都可以傳給這個(gè)函數(shù),,所以這個(gè)函數(shù)成為了一個(gè)通用的內(nèi)存復(fù)制函數(shù)。 好了,,說(shuō)了這么多,,回答最初的那個(gè)問(wèn)題上:
我們來(lái)分析一下。首先,,我們可以將這個(gè)數(shù)組看成是一個(gè)特殊的二維數(shù)組,,也就是1行5列的二維數(shù)組,現(xiàn)在a表示的是第一個(gè)元素的首地址,,那么a + 1指向的就是下一個(gè)元素的內(nèi)存首地址,,所以*(a + 1) = 2;而&a則是表示整個(gè)數(shù)組的首地址,,那么&a + 1移動(dòng)的內(nèi)存數(shù)目就是整個(gè)數(shù)組所占字節(jié)數(shù),,假如這里我們量化來(lái)說(shuō)明,假如原先數(shù)組中第一個(gè)元素的首地址是1,那么&a + 1表示的就是21,,而這個(gè)地址已經(jīng)不屬于數(shù)組了,,接著通過(guò)(int*)(&a + 1)將數(shù)組指針轉(zhuǎn)換成整型指針,這樣原先&a + 1表示的數(shù)據(jù)范圍是21~40一下縮小到21~24,正好是一個(gè)int型的大小,,所以ptr - 1的存儲(chǔ)的地址就是17了,,表示的數(shù)據(jù)內(nèi)存范圍是17~20,這樣*(ptr - 1)正好就是最后一個(gè)元素5了。 但是話說(shuō)回來(lái),首先這樣的轉(zhuǎn)換安全與否尚有爭(zhēng)議,,再次,,這樣的程序晦澀難懂,難于理解,,所以建議不要寫(xiě)出這樣的程序,。 上面的例子,只是通過(guò)一些簡(jiǎn)單的數(shù)據(jù)類(lèi)型來(lái)說(shuō)明內(nèi)存分布,,但是實(shí)際對(duì)于一些復(fù)雜的數(shù)據(jù)類(lèi)型,,尤其是一些自定義的類(lèi)或者結(jié)構(gòu)體類(lèi)型,內(nèi)存分布必須還要充分考慮到字節(jié)對(duì)齊,。比如下面的代碼: 這是輸出結(jié)果: 由于結(jié)構(gòu)體s1中存在字節(jié)對(duì)齊現(xiàn)象(以sizeof(double) = 8個(gè)字節(jié)對(duì)齊),,所以s1占據(jù)24字節(jié)內(nèi)存,而s2只占16個(gè)字節(jié),。知道這點(diǎn),,我們平常在設(shè)計(jì)結(jié)構(gòu)體字段的時(shí)候,就可以合理安排字段順序來(lái)使用更少的內(nèi)存空間了,。
函數(shù)指針函數(shù)指針是指向函數(shù)的指針變量,。 因而“函數(shù)指針”本身首先應(yīng)是指針變量,只不過(guò)該指針變量指向函數(shù),。這正如用指針變量可指向整型變量,、字符型、數(shù)組一樣,,這里是指向函數(shù),。C/C++程序在編譯時(shí),每一個(gè)函數(shù)都有一個(gè)入口地址,,該入口地址就是函數(shù)指針?biāo)赶虻牡刂?。有了指向函?shù)的指針變量后,可用該指針變量調(diào)用函數(shù),,就如同用指針變量可引用其他類(lèi)型變量一樣,,在這些概念上是一致的,。函數(shù)指針有兩個(gè)用途:調(diào)用函數(shù)和做函數(shù)的參數(shù),。 我們先來(lái)先使用函數(shù)指針調(diào)用函數(shù)。如下圖所示: 上面的代碼首先是定義了一個(gè)函數(shù)f,,然后是定義一個(gè)函數(shù)指針pf,,接著在主函數(shù)里面將函數(shù)f的地址賦值給函數(shù)指針,這樣pf就指向了函數(shù)f,,這樣使用*pf就可以直接調(diào)用函數(shù)了,。但是上面的例子定義函數(shù)指針的方法在某些編譯器中是無(wú)法通過(guò)的,最好通過(guò)typedef關(guān)鍵字定義函數(shù)指針,推薦的寫(xiě)法如下: 通過(guò)上面的例子,,我們來(lái)總結(jié)下函數(shù)指針的定義和使用方法: 首先,,通過(guò)typedef關(guān)鍵字定義一個(gè)函數(shù)指針類(lèi)型,然后定義一個(gè)該函數(shù)指針類(lèi)型變量,,接著將函數(shù)的入口地址賦值給該函數(shù)指針類(lèi)型變量,,這樣就可以通過(guò)這個(gè)函數(shù)指針變量調(diào)用函數(shù)了。 需要注意的是,,定義函數(shù)指針類(lèi)型時(shí)的函數(shù)簽名(包括函數(shù)返回值和函數(shù)參數(shù)列表的類(lèi)型,、個(gè)數(shù)、順序)要將賦值給該類(lèi)型變量的函數(shù)簽名保持一致,,不然可能會(huì)發(fā)生很多無(wú)法預(yù)料的情況,。還有一點(diǎn),就是C/C++規(guī)定函數(shù)名就表示函數(shù)入口地址,,所以,,函數(shù)名賦值時(shí)函數(shù)名前面加不加取地址符&都一樣,,也就是說(shuō)PF pf = f等價(jià)于PF pf = &f,。這個(gè)&是可以省略的,。但是這是單個(gè)函數(shù)的情況,,在C++中取類(lèi)的方法函數(shù)的地址時(shí),,這個(gè)&符號(hào)式不能省略的,,見(jiàn)下面的例子: 函數(shù)指針的另外一個(gè)用處,,而且是用的最多的,,就是作為一個(gè)函數(shù)的參數(shù),。也就是說(shuō)某個(gè)函數(shù)的某個(gè)參數(shù)類(lèi)型是一個(gè)函數(shù),,這在windows編程中作為回調(diào)函數(shù)(callback)尤其常見(jiàn)。我們來(lái)看一個(gè)例子: 上圖中,,函數(shù)f2第一個(gè)參數(shù)類(lèi)型是一個(gè)函數(shù),,我們傳入函數(shù)f1作為參數(shù)。這種函數(shù)參數(shù)是函數(shù)類(lèi)型的用法很重要,,建議大家掌握,。
指針變量的定義方法先插播一段廣告,說(shuō)下main函數(shù)的返回值問(wèn)題,,如下圖: 這種main函數(shù)無(wú)返回值的寫(xiě)法,,在國(guó)內(nèi)各大C/C++教材上屢見(jiàn)不鮮,這種寫(xiě)法是錯(cuò)誤的,! 有一點(diǎn)你必須明確:C/C++標(biāo)準(zhǔn)中從來(lái)沒(méi)有定義過(guò)void main()這樣的代碼形式,。C++之父Bjarne Stroustrup在他的主頁(yè)FAQ中明確地寫(xiě)了這樣一句話: 在C++中絕對(duì)沒(méi)有出現(xiàn)過(guò)void main(){ /* ... */ } 這樣的函數(shù)定義,在C語(yǔ)言中也是,。 main函數(shù)的返回值應(yīng)該定義為int型,,在C/C++標(biāo)準(zhǔn)中都是這樣規(guī)定的,。在C99標(biāo)準(zhǔn)規(guī)定,只有以下兩種定義方式是正確的的: 1 int main(void); 雖然在C和C++標(biāo)準(zhǔn)中并不支持void main(),,但是在部分編譯器中void main()依舊是可以通過(guò)編譯并執(zhí)行的,,比如微軟的VC++。由于微軟產(chǎn)品的市場(chǎng)占有率和影響力很大,,因?yàn)樵谀撤N程度上加劇了這種不良習(xí)慣的蔓延,。不過(guò),并非所有犯人編譯器都支持void main(),,gcc就站在VC++的對(duì)立面,,它是這一不良習(xí)氣的堅(jiān)定抵制者,它會(huì)在編譯時(shí)明確地給出一個(gè)錯(cuò)誤,。 廣告播完,,我們回到正題上來(lái)。我們來(lái)看下如何定義一個(gè)指針,,首先看一個(gè)例子: 我來(lái)替你回答吧,,你肯定認(rèn)為a是一個(gè)指針變量,b是一個(gè)整型變量,,c和d都是一個(gè)指針變量,。好吧,恭喜你,,答錯(cuò)了,! 其實(shí)定義指針變量的時(shí)候,星號(hào)(*)無(wú)論是與數(shù)據(jù)類(lèi)型結(jié)合還是與變量名結(jié)合在一起都是一樣的,!但是,,為了便于理解,還是推薦大家寫(xiě)成第一種形式,,第二種形式容易誤導(dǎo)人,,不是嗎?而且第一種形式還有一個(gè)好處,,我們可以這樣看: int *a; //將*a看成一個(gè)整體,,它是一個(gè)int型數(shù)據(jù),那么a自然就是指向*a的指針了,。 說(shuō)完定義指針的方法,,下面我們來(lái)看下如何初始化一個(gè)指針變量,看下面的代碼: 上面的代碼有錯(cuò)誤嗎,? 錯(cuò)誤在于我們不能這樣寫(xiě):int *p = 1; 由于p是一個(gè)匿名指針,,也就是說(shuō)p沒(méi)有正確的初始化,它可能指向一個(gè)不確定的內(nèi)存地址,,而這個(gè)內(nèi)存地址可能是系統(tǒng)程序內(nèi)存所在,,我們將數(shù)值1裝入那個(gè)不確定的內(nèi)存單元中是很危險(xiǎn)的,因?yàn)榭赡軙?huì)破壞系統(tǒng)那個(gè)內(nèi)存原來(lái)的數(shù)據(jù),,引發(fā)異常,。換另一個(gè)方面來(lái)看,將整型數(shù)值1直接賦值給指針型變量p是非法的,。 這樣的指針我們稱(chēng)為匿名指針或者野指針,。和其他變量類(lèi)型一樣,為了防止發(fā)生意料之外的錯(cuò)誤,,我們應(yīng)該給新定義的指針變量一個(gè)初始值,。但是有時(shí)候,我們又沒(méi)有合適的初始值給這個(gè)指針,,怎么辦,?我們可以使用NULL關(guān)鍵字或者C++中的nullptr。代碼如下: 通過(guò)上面的寫(xiě)法就告訴編譯器,,這兩個(gè)指針現(xiàn)在不會(huì)指向不確定的內(nèi)存單元了,,但是目前暫時(shí)不需要使用它們?!?/span>
C++中的引用C++中不僅有指針的概念,,而且還存在一個(gè)引用的概念,看下面的代碼: 我開(kāi)始在接觸這個(gè)概念的時(shí)候,,老是弄錯(cuò),。當(dāng)時(shí)這么想的,既然b是a的引用,,那么&b應(yīng)該等于a吧?也就是說(shuō),,在需要使用變量a的時(shí)候,可以使用&b來(lái)代替,。 上面的這種認(rèn)識(shí)是錯(cuò)誤的,!所謂引用,使用另外一個(gè)變量名來(lái)代表某一塊內(nèi)存,,也就是說(shuō)a和b完全是一樣,,所以任何地方,可以使用a的,,換成b也可以,,而不是使用&b,這就相當(dāng)于同一個(gè)人有不同的名字,,但是不管哪個(gè)名字,,指的都是同一個(gè)人。 新手在剛接觸引用的使用,,還有一個(gè)地方容易出錯(cuò),,就是忘記給引用及時(shí)初始化,,注意這里的“及時(shí)”兩個(gè)字,C++規(guī)定,,定義一個(gè)引用時(shí),,必須馬上初始化??聪旅娴拇a: 傳值還是傳引用(by value or by reference)看下面的偽代碼: 在涉及到利用一個(gè)已有初值的變量給另外一個(gè)變量賦值時(shí),,必須考慮這樣的情況。圖中變量a已經(jīng)有了初值,,然后利用a來(lái)給b賦初值,,那么最后改變b的值,a的值會(huì)不會(huì)受影響呢,?這就取決于b到底是a的副本還是和a同時(shí)指向同一內(nèi)存區(qū)域,,這就是我們常說(shuō)的賦值時(shí)是傳值還是傳引用。各大語(yǔ)言都是這樣規(guī)定的,,也就是說(shuō)不局限于C/C++,,同時(shí)Java、C#,、php,、javascript等都一樣: 如果變量類(lèi)型是基元數(shù)據(jù)類(lèi)型(基礎(chǔ)數(shù)據(jù)類(lèi)型),比如int,、float,、bool、char等小數(shù)據(jù)類(lèi)型被稱(chēng)為基元數(shù)據(jù)類(lèi)型(primitive data type),,那么賦值時(shí)傳的是值,。也就是說(shuō),這個(gè)時(shí)候b的值是a的拷貝,,那么更改b不會(huì)影響到a,,同理更改a也不會(huì)影響到b。 但是,,如果變量類(lèi)型是復(fù)雜數(shù)據(jù)類(lèi)型(complex data type),,不如數(shù)組、類(lèi)對(duì)象,,那么賦值時(shí)傳的就是引用,,這個(gè)時(shí)候,a和b指向的都是同一塊內(nèi)存區(qū)域,,那么無(wú)論更改a或者b都會(huì)相互影響,。 讓我們來(lái)深入地分析下,為什么各大語(yǔ)言都采取這種機(jī)制,。對(duì)于那些基元數(shù)據(jù)類(lèi)型,,由于數(shù)據(jù)本身占用的內(nèi)存空間就小,,這樣復(fù)制起來(lái)不僅速度快,即使這樣的變量數(shù)目很多,,總共也不會(huì)占多大空間,。但是對(duì)于復(fù)雜數(shù)據(jù)類(lèi)型,,比如一些類(lèi)對(duì)象,,它們包含的屬性字段就很多,占用的空間就大,,如果賦值時(shí),,也是復(fù)制數(shù)據(jù),那么一個(gè)兩個(gè)對(duì)象還好,,一旦多一點(diǎn)比如10個(gè),、100個(gè),會(huì)占很大的內(nèi)存單元的,,這就導(dǎo)致效率的下降,。 最后,提醒一點(diǎn),,在利用C++中拷貝構(gòu)造函數(shù)復(fù)制對(duì)象時(shí)需要注意,,基元數(shù)據(jù)類(lèi)型可以直接復(fù)制,但是對(duì)于引用類(lèi)型數(shù)據(jù),,我們需要自己實(shí)現(xiàn)引用型數(shù)據(jù)的真正復(fù)制,。
C/C++中的new關(guān)鍵字與Java、C#中的關(guān)鍵字對(duì)比我大學(xué)畢業(yè)的時(shí)候癡迷于于網(wǎng)頁(yè)游戲開(kāi)發(fā),,使用的語(yǔ)言是flash平臺(tái)的actionscript 3.0(簡(jiǎn)稱(chēng)as3,,唉,如今已經(jīng)沒(méi)落),,我剛開(kāi)始由as3轉(zhuǎn)行至C/C++,,對(duì)于C/C++中new出來(lái)的對(duì)象必須通過(guò)指針對(duì)象來(lái)引用它非常不習(xí)慣。上圖中,Object是一個(gè)類(lèi)(class),,在Java或者C#等語(yǔ)言中,,通過(guò)new關(guān)鍵字定義一個(gè)對(duì)象,直接得到Object的實(shí)例,,也就是說(shuō)后續(xù)引用這個(gè)對(duì)象,,我們可以直接使用obj.property或者obj.method()等形式,但是在C++中不行,,比如用一個(gè)指針去接受這個(gè)new出來(lái)的對(duì)象,,我們引用這個(gè)對(duì)象必須使用指針引用運(yùn)算符->,也就是我們需要這樣寫(xiě):pObj->property或pObject->method(),。代碼如下: 當(dāng)然C++中還有一種不需要使用指針就可以實(shí)例化出來(lái)類(lèi)對(duì)象的方法,,從Java,、C#等轉(zhuǎn)向C++的程序員容易誤解為未初始化對(duì)象變量的定義,看下列代碼: 這是C++中利用Object類(lèi)實(shí)例化兩個(gè)對(duì)象obj1和obj2,,obj2因?yàn)檎{(diào)用構(gòu)造函數(shù)傳了兩個(gè)參數(shù)param1,param2還好理解一點(diǎn),,對(duì)于obj1很多Java或者C#的程序員開(kāi)始很難接受這種寫(xiě)法,因?yàn)槿绻旁贘ava或者C#中,,obj1根本就沒(méi)有被實(shí)例化嘛,,在他們看來(lái),obj1只是一個(gè)簡(jiǎn)單的類(lèi)型申明,。希望Java,、C#等程序員要轉(zhuǎn)換過(guò)思維來(lái)看待C++中的這種寫(xiě)法。 還有一點(diǎn)也容易出錯(cuò),,在C++中,,this關(guān)鍵字是一個(gè)指針,而不是像在Java,、C#中是一個(gè)類(lèi)實(shí)例,。也就是說(shuō),在C++中*this才等價(jià)于Java,、C#中的this,。所以寫(xiě)法也就不一樣了:
Windows編程中的指針windows是操作系統(tǒng)是用C語(yǔ)言寫(xiě)出來(lái)的,所以盡管你在windows中看到很多不認(rèn)識(shí)的數(shù)據(jù)類(lèi)型,,但是這些數(shù)據(jù)類(lèi)型也是通過(guò)基本的C語(yǔ)言類(lèi)型組裝起來(lái)的,。我們這里只介紹windows中指針型數(shù)據(jù)。 定義指針數(shù)據(jù)類(lèi)型必須使用星號(hào)(*),,但是windows為了開(kāi)發(fā)的方便,,通過(guò)宏定義將指針“隱藏起來(lái)”,嚴(yán)格地說(shuō)應(yīng)該是將星號(hào)隱藏起來(lái)了,,下面給出一些例子:
C++中的智能指針為了保持內(nèi)容的完整性,,暫且列一個(gè)標(biāo)題放在這里,這個(gè)話題以后探討吧,。
我能想到的關(guān)于C/C++中指針的內(nèi)容就這么多了,,希望本文對(duì)你有用。文中如果有不當(dāng)或者紕漏的地方歡迎批評(píng)指正,。另外,,感謝學(xué)生時(shí)代的小雷姑娘的支持和鼓勵(lì),哈~ |
|
來(lái)自: 太極混元天尊 > 《學(xué)習(xí)資料》