鍍金池/ 教程/ C/ 數(shù)組與指針(一)
寫在前面的小測(cè)試
那些不安全的庫函數(shù)
數(shù)組與指針(一)
非局部跳轉(zhuǎn)
數(shù)組與指針的糾葛
輸出
類型轉(zhuǎn)換
函數(shù)指針
預(yù)處理

數(shù)組與指針(一)

指針是 C 的精華,如果未能很好地掌握指針,那 C 也基本等于沒學(xué)。

先附上兩句話:
第一句話:指針就是存放地址的變量。(就是這么簡(jiǎn)單。)
第二句話:指針是指針,數(shù)組是數(shù)組。(只是它們經(jīng)常穿著相似的衣服來逗你玩罷了。)

輕松一下:(見識(shí)一下數(shù)組和指針的把戲)
1、引用一維數(shù)組某個(gè)值的方式:(先定義指針p=a)

  • a[2]
  • *(a+2)
  • (&a[1])[1]
  • *(p+2)
  • p[2]

2、引用二維數(shù)組某個(gè)值的方式:

例:int a[4][5];

  • ⑴ a[i][j]
  • ⑵ *(a[i]+j)
  • ⑶ *(*(a+i)+j)
  • ⑷ (*(a+i))[j]
  • ⑸ *(&a[0][0]+i*5+j)

若定義:int * p[4], m ;
for(m=0; m<4;m++) p[m] = a[m] ;

  • ⑹ p[i][j]
  • ⑺ *(p[i]+j)
  • ⑻ *(*(p+i)+j)
  • ⑼ (*(p+i))[j] //請(qǐng)與⑴-⑷對(duì)比

若定義 int (*q)[5]; q=a ;

  • ⑽ q[i][j]
  • ⑾ *(q[i]+j)
  • ⑿ *(*(q+i)+j)
  • ⒀ (*(q+i))[j] //請(qǐng)與⑴-⑷ ⑹-⑼對(duì)比

進(jìn)入正題:

數(shù)組:

數(shù)組是指具有相同類型的數(shù)據(jù)組成的序列,是有序集合。(教科書上的定義)
(即:數(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)著首元素的地址。 所以,我們可以通過數(shù)組的首元素地址來找到數(shù)組)

故:數(shù)組名是一個(gè)地址(首元素地址),即是一個(gè)指針常量。(不是指針變量)
只有在兩種場(chǎng)合下,數(shù)組名并不用指針常量來表示:

  1. sizeof(數(shù)組名) ; sizeof返回整個(gè)數(shù)組的長度,而不是指向數(shù)組的指針長度。
  2. &數(shù)組名 ; 產(chǎn)生的是一個(gè)指向整個(gè)數(shù)組的指針,而不是一個(gè)指向某個(gè)指針常量的指針。

&a[0] 與 &a 的區(qū)別
兩者的值相同,但意義不同。
&a[0]是指數(shù)組首元素的地址。&a是整個(gè)數(shù)組的地址。
(問題來了,整個(gè)數(shù)組跨越幾個(gè)存儲(chǔ)單位,怎么表示這幾個(gè)存儲(chǔ)單位組成的整體呢?如果你是編譯器,你會(huì)怎么做?呃,取其第一個(gè)存儲(chǔ)單位的值來代表會(huì)比較好點(diǎn)。沒錯(cuò),編譯器是這么做的。 所以兩者的值相同)

a+1 與 &a+1 的區(qū)別
數(shù)組名a除了在上述兩種情況下,均用&a[0]來代替。(實(shí)際上編譯器也是這么做的)
a+1即等同于&a[0]+1。

注意:指針(地址)與常數(shù)相加減,不是簡(jiǎn)單地算術(shù)運(yùn)算,而是以當(dāng)前指針指向的對(duì)象的存儲(chǔ)長度為單位來計(jì)算的。
即:指向的地址+常數(shù)*(指向的對(duì)象的存儲(chǔ)長度)

&a[0]為數(shù)組首元素的地址,故&a[0]+1 越過一個(gè)數(shù)組元素長度的位置。即:&a[0]+1*sizeof(a[0])
&a為整個(gè)數(shù)組的地址,(只是用首元素地址來表示,其實(shí)際代表的意義是整個(gè)數(shù)組)
故&a+1 越過整個(gè)數(shù)組長度的位置,到達(dá)數(shù)組a后面第一個(gè)位置。 即:&a+1*sizeof(a)

指針

定義與解引用

int *p = NULL; 與 *p = NULL ;
指針的定義與解引用都用到 ,這是讓人暈的一個(gè)地方。
(不妨這樣理解:在定義時(shí),星號(hào)只是表示這是一個(gè)指針,int
表示這是一個(gè)int型的指針,把int * 放在一起看,表示這是一個(gè)整型指針類型。如果我是 C 的設(shè)計(jì)者,那么用$符號(hào)來定義指針類型 會(huì)不會(huì)讓大家少些迷惑)

向指針變量賦值,右值必須是一個(gè)地址。例:int p = &i ;
這樣,編譯器在變量表里查詢變量 i 對(duì)應(yīng)的地址,然后用地址值把 &i 替換掉。 那么我們能不能直接把地址值寫出來作為右值呢?當(dāng)然。指針不就是存儲(chǔ)地址的變量嘛,直接把數(shù)字型的地址值賦給它有什么問題。(前提是這個(gè)地址值必須是程序可訪問的)
例:
int
p = (int )0x12ff7c ;
p = 0x100 ;
這里的 0x12ff7c 可看做某個(gè)變量的地址。 需要注意的是:將地址 0x12ff7c 賦值給指針變量 p 的時(shí)候必須強(qiáng)制轉(zhuǎn)換。(我們要保證賦值號(hào)兩邊的數(shù)據(jù)類型一致)

地址的強(qiáng)制轉(zhuǎn)換

例: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)換成無符號(hào)的長整型。(即:告訴編譯器,以前變量p里存儲(chǔ)的是內(nèi)存中的某個(gè)地址,現(xiàn)在變量p里存儲(chǔ)的是一個(gè)長整型。即讓編譯器看待變量 p 的眼光改變一下,以后p是一個(gè)整型變量了,不是指針了,不要把它里面的值當(dāng)做某個(gè)變量的地址了,不能根據(jù)這個(gè)地址去找某變量了。)
任何數(shù)值一旦被強(qiáng)制轉(zhuǎn)換,其類型就變了。即編譯器解釋其值代表的含義就變了。
故:(unsignedlong)p + 0x1 是一個(gè)長整型值加一個(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)換指針類型的目的是為了:改變指針的步長(偏移的單位長度)

注意:
兩個(gè)指針直接相加是不允許的。(你要真想把兩個(gè)地址值相加,把它們先都強(qiáng)制轉(zhuǎn)換為int型即可)
兩個(gè)指針直接相減在語法上是允許的。(但必須相對(duì)于同一個(gè)數(shù)組,結(jié)果是兩指針指向位置相隔的元素個(gè)數(shù))

指針表達(dá)式

注意:*與++優(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ū)中,故無法得到它們的地址,它們也無法做左值】 ★故:(\
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