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