指針是 C 的精華
,如果未能很好地掌握指針,那 C 也基本等于沒(méi)學(xué)。
先附上兩句話(huà):
第一句話(huà):指針就是存放地址的變量。(就是這么簡(jiǎn)單。)
第二句話(huà):指針是指針,數(shù)組是數(shù)組。(只是它們經(jīng)常穿著相似的衣服來(lái)逗你玩罷了。)
輕松一下:(見(jiàn)識(shí)一下數(shù)組和指針的把戲)
1、引用一維數(shù)組
某個(gè)值的方式:(先定義指針p=a)
2、引用二維數(shù)組
某個(gè)值的方式:
例:int a[4][5];
若定義:int * p[4], m ;
for(m=0; m<4;m++) p[m] = a[m] ;
若定義 int (*q)[5]; q=a ;
數(shù)組是指具有相同類(lèi)型的數(shù)據(jù)組成的序列,是有序集合。(教科書(shū)上的定義)
(即:數(shù)組就是內(nèi)存中一段連續(xù)的存儲(chǔ)空間。那么我們?cè)趺词褂盟??用?shù)組名。也就是我們用數(shù)組名可以在內(nèi)存中找到對(duì)應(yīng)的數(shù)組空間,即數(shù)組名對(duì)應(yīng)著地址。
那么數(shù)組中有這么多元素,對(duì)應(yīng)的是哪個(gè)元素的地址呢?對(duì)應(yīng)著首元素的地址。 所以,我們可以通過(guò)數(shù)組的首元素地址來(lái)找到數(shù)組)
故:數(shù)組名是一個(gè)地址(首元素地址),即是一個(gè)指針常量。(不是指針變量)
只有在兩種場(chǎng)合下,數(shù)組名并不用指針常量來(lái)表示:
&a[0] 與 &a 的區(qū)別
:
兩者的值相同,但意義不同。
&a[0]是指數(shù)組首元素的地址。&a是整個(gè)數(shù)組的地址。
(問(wèn)題來(lái)了,整個(gè)數(shù)組跨越幾個(gè)存儲(chǔ)單位,怎么表示這幾個(gè)存儲(chǔ)單位組成的整體呢?如果你是編譯器,你會(huì)怎么做?呃,取其第一個(gè)存儲(chǔ)單位的值來(lái)代表會(huì)比較好點(diǎn)。沒(méi)錯(cuò),編譯器是這么做的。 所以?xún)烧叩闹迪嗤?
a+1 與 &a+1 的區(qū)別
:
數(shù)組名a除了在上述兩種情況下,均用&a[0]來(lái)代替。(實(shí)際上編譯器也是這么做的)
a+1即等同于&a[0]+1。
注意:指針(地址)與常數(shù)相加減,不是簡(jiǎn)單地算術(shù)運(yùn)算,而是以當(dāng)前指針指向的對(duì)象的存儲(chǔ)長(zhǎng)度為單位來(lái)計(jì)算的。
即:指向的地址+常數(shù)*(指向的對(duì)象的存儲(chǔ)長(zhǎng)度)
&a[0]為數(shù)組首元素的地址,故&a[0]+1 越過(guò)一個(gè)數(shù)組元素長(zhǎng)度的位置。即:&a[0]+1*sizeof(a[0])
&a為整個(gè)數(shù)組的地址,(只是用首元素地址來(lái)表示,其實(shí)際代表的意義是整個(gè)數(shù)組)
故&a+1 越過(guò)整個(gè)數(shù)組長(zhǎng)度的位置,到達(dá)數(shù)組a后面第一個(gè)位置。 即:&a+1*sizeof(a)
int *p = NULL; 與 *p = NULL ;
指針的定義與解引用都用到 ,這是讓人暈的一個(gè)地方。
(不妨這樣理解:在定義時(shí),星號(hào)只是表示這是一個(gè)指針,int 表示這是一個(gè)int型的指針,把int * 放在一起看,表示這是一個(gè)整型指針類(lèi)型。如果我是 C 的設(shè)計(jì)者,那么用$符號(hào)來(lái)定義指針類(lèi)型 會(huì)不會(huì)讓大家少些迷惑)
向指針變量賦值,右值必須是一個(gè)地址。例:int p = &i ;
這樣,編譯器在變量表里查詢(xún)變量 i 對(duì)應(yīng)的地址,然后用地址值把 &i 替換掉。 那么我們能不能直接把地址值寫(xiě)出來(lái)作為右值呢?當(dāng)然。指針不就是存儲(chǔ)地址的變量嘛,直接把數(shù)字型的地址值賦給它有什么問(wèn)題。(前提是這個(gè)地址值必須是程序可訪問(wèn)的)
例:
int p = (int )0x12ff7c ;
p = 0x100 ;
這里的 0x12ff7c 可看做某個(gè)變量的地址。需要注意的是:將地址 0x12ff7c 賦值給指針變量 p 的時(shí)候必須強(qiáng)制轉(zhuǎn)換。(我們要保證賦值號(hào)兩邊的數(shù)據(jù)類(lèi)型一致)
例:double p ;假設(shè)p的值為 0x100000
求下列表達(dá)式的值:
p + 0x1 =
(unsigned long)p + 0x1 =
(unsigned int )p + 0x1 = ___
注意:
一個(gè)指針與一個(gè)整數(shù)相加減。這個(gè)整數(shù)的單位不是字節(jié),而是指針?biāo)赶虻脑氐膶?shí)際存儲(chǔ)大小
。
所以 p + 0x1,p 指向的是一個(gè) double 型變量,故值應(yīng)為:0x100000+0x1*8=0x100008
(unsigned long)p則意為:將表示地址值的 p 強(qiáng)制轉(zhuǎn)換成無(wú)符號(hào)的長(zhǎng)整型。(即:告訴編譯器,以前變量p里存儲(chǔ)的是內(nèi)存中的某個(gè)地址,現(xiàn)在變量p里存儲(chǔ)的是一個(gè)長(zhǎng)整型。即讓編譯器看待變量 p 的眼光改變一下
,以后p是一個(gè)整型變量了,不是指針了,不要把它里面的值當(dāng)做某個(gè)變量的地址了,不能根據(jù)這個(gè)地址去找某變量了。)
任何數(shù)值一旦被強(qiáng)制轉(zhuǎn)換,其類(lèi)型就變了。即編譯器解釋其值代表的含義就變了。
故:(unsignedlong)p + 0x1 是一個(gè)長(zhǎng)整型值加一個(gè)整型值,結(jié)果為:0x100001
(unsigned int *)p則意為:將一個(gè)表示double型變量的地址值的指針,轉(zhuǎn)換成一個(gè)表示unsigned int型變量地址的指針。
故(unsigned int*)p + 0x1 值為:0x100000+sizeof(unsignedint)*0x1 等于 0x100004
【強(qiáng)制轉(zhuǎn)換指針類(lèi)型的目的是為了:改變指針的步長(zhǎng)(偏移的單位長(zhǎng)度)】
注意:
兩個(gè)指針直接相加是不允許的。(你要真想把兩個(gè)地址值相加,把它們先都強(qiáng)制轉(zhuǎn)換為int型即可)
兩個(gè)指針直接相減在語(yǔ)法上是允許的。(但必須相對(duì)于同一個(gè)數(shù)組,結(jié)果是兩指針指向位置相隔的元素個(gè)數(shù))
注意:*與++優(yōu)先級(jí)相同,且它們的結(jié)合性都是從右向左的。
例:char ch ;char *cp=&ch ;
指針表達(dá)式:
*`++cp`* 先運(yùn)算++cp,再解引用\。
當(dāng)其為右值時(shí),是ch下一個(gè)存儲(chǔ)單元的值(是一個(gè)垃圾值)
當(dāng)其為左值時(shí),是ch的下一個(gè)存儲(chǔ)單元
*`(cp)++`*當(dāng)其為右值時(shí),表達(dá)式的值等于ch的值,(但它使ch值自增1)
當(dāng)其為左值時(shí),非法。
【注意:++,--的表達(dá)式(及大部分的表達(dá)式,數(shù)組的后綴表達(dá)式除外)的值都只是一種映像(暫存于寄存器),不在內(nèi)存區(qū)中,故無(wú)法得到它們的地址,它們也無(wú)法做左值】★故:(\cp)++表達(dá)式的值雖與ch相同,但它只是ch值的一份拷貝,不是真正的ch
*`++cp++`**
當(dāng)其為右值時(shí):表達(dá)式的值等于ch+1,這個(gè)值只是一個(gè)映像(寄存器中)。(但這個(gè)表達(dá)式實(shí)際做了一些工作:使cp指向ch的下一個(gè)單元,使ch中的值增1)
當(dāng)其為左值時(shí),非法。
【++,--,與 * 組合的指針表達(dá)式只是把幾個(gè)工作融合在一個(gè)表達(dá)式中完成,使代碼簡(jiǎn)潔,但可讀性差】
例:對(duì)于 *cp++ ; 我們可以把它分解為: *cp 之后再 cp++
對(duì)于 *++cp ; 我們可以把它分解為:++cp 之后再*cp
更多建議: