李 瑛
(太原師范學(xué)院 計(jì)算機(jī)系,山西 太原 030001)
淺析C程序設(shè)計(jì)中指針的奧秘
李 瑛
(太原師范學(xué)院 計(jì)算機(jī)系,山西 太原 030001)
在C語(yǔ)言中,指針是廣泛使用的一種數(shù)據(jù)類型,起著不可低估的作用,同時(shí)它也是C語(yǔ)言的一個(gè)精華,極大地豐富了C語(yǔ)言的功能。文章結(jié)合作者的教學(xué)實(shí)踐經(jīng)驗(yàn),針對(duì)學(xué)生在學(xué)習(xí)C語(yǔ)言指針時(shí)遇到的問(wèn)題進(jìn)行總結(jié)和歸納。便于為以后的教學(xué)提供參考。
指針;變量;地址;C語(yǔ)言
在C語(yǔ)言中,指針是廣泛使用的一種數(shù)據(jù)類型,起著不可低估的作用,同時(shí)它也是C語(yǔ)言的一個(gè)精華。有人說(shuō),沒有學(xué)過(guò)指針就沒有學(xué)過(guò)C語(yǔ)言,可見,指針在C語(yǔ)言中作用是不可替代的。利用指針變量可以表示各種數(shù)據(jù)結(jié)構(gòu);能方便地使用數(shù)組和字符串;并能象匯編語(yǔ)言一樣處理內(nèi)存地址,從而編出精練而高效的程序。在C語(yǔ)言程序設(shè)計(jì)中,指針極大地豐富了C語(yǔ)言的功能。能否正確理解和使用指針是我們是否掌握C語(yǔ)言的一個(gè)標(biāo)志。但是由于指針太過(guò)靈活,一旦使用不當(dāng),不僅錯(cuò)誤難查,而且會(huì)出現(xiàn)意想不到的錯(cuò)誤。因此,要編出正確而高效的程序必須正確地理解和使用指針。針對(duì)初學(xué)者對(duì)C語(yǔ)言指針的理解不夠透徹,作者根據(jù)在C語(yǔ)言教學(xué)和使用方面的經(jīng)驗(yàn),對(duì)指針做了詳細(xì)的闡述。
指針是一種特殊的變量,它里面存儲(chǔ)的數(shù)值被解釋成為內(nèi)存里的一個(gè)地址。對(duì)指針的理解需要搞清四方面的內(nèi)容:指針的類型;指針?biāo)赶虻念愋?;指針的值或者叫指針?biāo)赶虻膬?nèi)存區(qū);指針本身所占據(jù)的內(nèi)存區(qū)。
從語(yǔ)法的角度看,只要把指針聲明語(yǔ)句里的指針名字去掉,剩下的部分就是這個(gè)指針的類型。這是指針本身所具有的類型。讓我們看看例中指針的類型:
(1)int*pointer; //指針的類型是 int*
(2)int(*pointer)[3];//指針的類型是int(*)[3]
當(dāng)通過(guò)指針來(lái)訪問(wèn)指針?biāo)赶虻膬?nèi)存區(qū)時(shí),指針?biāo)赶虻念愋蜎Q定了編譯器將把那片內(nèi)存區(qū)里的內(nèi)容當(dāng)做什么來(lái)看待。從語(yǔ)法上看,只須把指針聲明語(yǔ)句中的指針名字和名字左邊的指針聲明符*去掉,剩下的就是指針?biāo)赶虻念愋汀@纾?/p>
(1)int*pointer; //指針?biāo)赶虻念愋褪莍nt
(2)int(*pointer)[3];//指針?biāo)赶虻牡念愋褪莍nt()[3]
指針的類型(即指針本身的類型)和指針?biāo)赶虻念愋褪莾蓚€(gè)概念。對(duì)C越來(lái)越熟悉時(shí)就會(huì)發(fā)現(xiàn),把與指針攪和在一起的“類型”這個(gè)概念分成“指針的類型”和“指針?biāo)赶虻念愋汀眱蓚€(gè)概念,是精通指針的關(guān)鍵點(diǎn)之一。
指針的值是指針本身存儲(chǔ)的數(shù)值,這個(gè)值將被編譯器當(dāng)作一個(gè)地址,而不是一個(gè)一般的數(shù)值。指針?biāo)赶虻膬?nèi)存區(qū)就是從指針的值所代表的那個(gè)內(nèi)存地址開始,長(zhǎng)度為sizeof(指針?biāo)赶虻念愋停┑囊黄瑑?nèi)存區(qū)。以后,如果說(shuō)一個(gè)指針的值是XX,就相當(dāng)于說(shuō)該指針指向了以XX為首地址的一片內(nèi)存區(qū)域;一個(gè)指針指向了某塊內(nèi)存區(qū)域,就相當(dāng)于說(shuō)該指針的值是這塊內(nèi)存區(qū)域的首地址。
指針?biāo)赶虻膬?nèi)存區(qū)和指針?biāo)赶虻念愋褪莾蓚€(gè)完全不同的概念。在上面的例子中,指針?biāo)赶虻念愋鸵呀?jīng)有了,但由于指針還未初始化,因此它所指向的內(nèi)存區(qū)是不存在的,或者說(shuō)是無(wú)意義的。所以,每遇到一個(gè)指針,都應(yīng)該問(wèn)問(wèn):這個(gè)指針的類型是什么?指針指的類型是什么?該指針指向了哪里?
利用sizeof(指針的類型)函數(shù),可以測(cè)試指針本身所占內(nèi)存的大小,在16位平臺(tái)里,指針本身占據(jù)了2個(gè)字節(jié)的長(zhǎng)度。
指針是可以進(jìn)行的算術(shù)運(yùn)算的。指針可以加上或減去一個(gè)整數(shù)。指針的這種運(yùn)算的意義和通常的數(shù)值的加減運(yùn)算的意義是不一樣的。例如:
int a;int*pointer=a;pointer++;
在上例中,指針pointer的類型是int*,它指向的類型是int,它被初始化為指向整形變量a。接下來(lái)的語(yǔ)句中,指針pointer被加了1,編譯器是這樣處理的:它把指針pointer的值加上了sizeof(int)(在16位程序中,是被加上了2),由于地址是用字節(jié)做單位的,故pointer所指向的地址由原來(lái)的變量a的地址向高地址方向增加了2個(gè)字節(jié)。
用一個(gè)指針和一個(gè)循環(huán)來(lái)遍歷一個(gè)數(shù)組,例:int array[20];
這個(gè)例子將整型數(shù)組中各個(gè)元素的值加1。由于每次循環(huán)都將指針pointer加1所以每次循環(huán)都能訪問(wèn)數(shù)組的下一個(gè)元素。
所以,一個(gè)指針pointerold加上一個(gè)整數(shù)n后,結(jié)果是一個(gè)新的指針pointernew,pointernew的類型和pointerold的類型相同,pointernew所指向的類型和pointerold所指向的類型也相同。pointernew的值將比pointerold的值增加了n乘sizeof(pointerold所指向的類型)個(gè)字節(jié)。就是說(shuō),pointernew所指向的內(nèi)存區(qū)將比pointerold所指向的內(nèi)存區(qū)向高地址方向移動(dòng)了n乘sizeof(pointerold所指向的類型)個(gè)字節(jié)。一個(gè)指針pointerold減去一個(gè)整數(shù)n后,結(jié)果是一個(gè)新的指針pointernew,pointernew的類型和pointerold的類型相同,pointernew所指向的類型和pointerold所指向的類型也相同。pointernew的值將比pointerold的值減少了n乘sizeof(pointerold所指向的類型)個(gè)字節(jié),就是說(shuō),pointernew所指向的內(nèi)存區(qū)將比pointerold所指向的內(nèi)存區(qū)向低地址方向移動(dòng)了n乘sizeof(pointerold所指向的類型)個(gè)字節(jié)。
&是取地址運(yùn)算符。若a是一個(gè)變量,&a的運(yùn)算結(jié)果是一個(gè)指針,指針的類型是a的類型加個(gè)*,指針?biāo)赶虻念愋褪莂的類型,指針?biāo)赶虻牡刂?,那就是a的地址。
*p的結(jié)果是p所指向的內(nèi)容,并且具有如下特點(diǎn):它的類型是p指向的類型,它所占用的地址是p所指向的地址。例:
int a=12;int b;int*p;int**pointer;
p=&a;//&a的結(jié)果是一個(gè)指針,類型是int*,指向的類型是int,指向的地址是a的地址。
*p=24;//*p的結(jié)果,在這里它的類型是int,它所占用的地址是p所指向的地址,顯然,*p就是變量a。
*pointer=&b;//*pointer是個(gè)指針,&b 的結(jié)果也是個(gè)指針,且這兩個(gè)指針的類型和所指向的類型是一樣的,所以用&b來(lái)給*pointer賦值就是毫無(wú)問(wèn)題的了。
**pointer=34; //*pointer的結(jié)果是 pointer所指向的東西,在這里是一個(gè)指針,對(duì)這個(gè)指針再做一次*運(yùn)算,結(jié)果就是一個(gè)int類型的變量。
數(shù)組的數(shù)組名其實(shí)可以看作一個(gè)指針。如下例:
int array[10]={0,1,2,3,4,5,6,7,8,9},value;
value=array[0];//也可寫成:value=*array;
value=array[3];//也可寫成:value=*(array+3);
2.2 堅(jiān)持以人為本的原則,分層對(duì)待員工的各自情況。國(guó)有企業(yè)職工出身結(jié)構(gòu)的多樣性,往往體現(xiàn)在職工的年齡、學(xué)歷、經(jīng)歷、性別,家庭都是造成不同的思想狀態(tài)的因素,對(duì)職工進(jìn)行同類分類,單獨(dú)為同類職工制定相關(guān)的教育輔導(dǎo)方式,能夠解除職工的一些顧慮,真實(shí)準(zhǔn)確、及時(shí)地了解員工的思想動(dòng)態(tài),化解企業(yè)內(nèi)部的矛盾,營(yíng)造和諧融洽的企業(yè)工作環(huán)境,提高企業(yè)的生產(chǎn)經(jīng)營(yíng)效率。
上例中,一般而言數(shù)組名array代表數(shù)組本身,類型是int[10],但如果把a(bǔ)rray看做指針的話,它指向數(shù)組的第0個(gè)單元,類型是int*,所指向的類型是數(shù)組單元的類型,即int。因此*array等于0就一點(diǎn)也不奇怪了。同理,array+3是一個(gè)指向數(shù)組第3,所以*(array+3)等于3。其它依此類推。
聲明了一個(gè)數(shù)組TYPE array[n],則數(shù)組名稱array就有了兩重含義:第一,它代表整個(gè)數(shù)組,它的類型是TYPE[n];第二,它是一個(gè)指針,該指針的類型是TYPE*,該指針指向的類型是TYPE,也就是數(shù)組單元的類型,該指針指向的內(nèi)存區(qū)就是數(shù)組第0號(hào)單元,該指針自己占有單獨(dú)的內(nèi)存區(qū)。注意它和數(shù)組第0號(hào)單元占據(jù)的內(nèi)存區(qū)是不同的。該指針的值是不能修改的,即類似array++的表達(dá)式是錯(cuò)誤的。在不同的表達(dá)式中數(shù)組名array可以扮演不同的角色。在表達(dá)式sizeof(array)中,數(shù)組名array代表數(shù)組本身,故這時(shí)sizeof函數(shù)測(cè)出的是整個(gè)數(shù)組的大小。在表達(dá)式*array中,array扮演的是指針,因此這個(gè)表達(dá)式的結(jié)果就是數(shù)組第0號(hào)單元的值。sizeof(*array)測(cè)出的是數(shù)組單元的大小。表達(dá)式array+n(其中 n=0,1,2,....。)中,array扮演的是指針,故array+n的結(jié)果是一個(gè)指針,它的類型是TYPE*,它指向的類型是TYPE,它指向數(shù)組第n號(hào)單元。故sizeof(array+n)測(cè)出的是指針類型的大小。
當(dāng)我們初始化一個(gè)指針或給一個(gè)指針賦值時(shí),賦值號(hào)的左邊是一個(gè)指針,賦值號(hào)的右邊是一個(gè)指針表達(dá)式。在前面所舉的例子中,絕大多數(shù)情況下,指針的類型和指針表達(dá)式的類型是一樣的,指針?biāo)赶虻念愋秃椭羔槺磉_(dá)式所指向的類型是一樣的。例:
float f=12.3;float*fpointer=&f;int*p;
在上面的例子中,假如想讓指針p指向?qū)崝?shù)f,那么p=&f是錯(cuò)誤的。因?yàn)橹羔榩的類型是int*,它指向的類型是int。表達(dá)式&f的結(jié)果是一個(gè)指針,指針的類型是float*,它指向的類型是float。兩者不一致,直接賦值的方法是不行的。為了達(dá)到目的,需要進(jìn)行“強(qiáng)制類型轉(zhuǎn)換”:p=(int*)&f;如果有一個(gè)指針p,需要把它的類型和所指向的類型改為TYEP*TYPE,那么語(yǔ)法格式是:(TYPE*)p;
這樣強(qiáng)制類型轉(zhuǎn)換的結(jié)果是一個(gè)新指針,該新指針的類型是TYPE*,它指向的類型是TYPE,它指向的地址就是原指針指向的地址。而原來(lái)的指針p的一切屬性都沒有被修改。
看下面的例子:
char s='a'; int*pointer; pointer=(int*)&s;*pointer=1298;
指針pointer是一個(gè)int*類型的指針,它指向的類型是int。它指向的地址就是s的首地址。在32位程序中,s占一個(gè)字節(jié),int類型占四個(gè)字節(jié)。最后一條語(yǔ)句不但改變了s所占的一個(gè)字節(jié),還把和s相臨的高地址方向的三個(gè)字節(jié)也改變了。這三個(gè)字節(jié)是干什么的?只有編譯程序知道,而寫程序的人是不太可能知道的。也許這三個(gè)字節(jié)里存儲(chǔ)了非常重要的數(shù)據(jù),也許這三個(gè)字節(jié)里正好是程序的一條代碼,而由于對(duì)指針的馬虎應(yīng)用,這三個(gè)字節(jié)的值被改變了!這會(huì)造成崩潰性的錯(cuò)誤。
如下例:
1、char a;
2、int*pointer=&a;
3、pointer++;
4、*pointer=115;
該例子完全可以通過(guò)編譯,并能執(zhí)行。但是第3句對(duì)指針pointer進(jìn)行自加1運(yùn)算后,pointer指向了和整形變量a相鄰的高地址方向的一塊存儲(chǔ)區(qū)。這塊存儲(chǔ)區(qū)里的具體內(nèi)容并不明確,有可能它是一個(gè)非常重要的數(shù)據(jù),甚至可能是一條代碼。而第4句竟然往這片存儲(chǔ)區(qū)里寫入一個(gè)數(shù)據(jù)!這是嚴(yán)重的錯(cuò)誤。所以在使用指針時(shí),程序員心里必須非常清楚指針的指向。在用指針訪問(wèn)數(shù)組的時(shí)候,也要注意不要超出數(shù)組的低端和高端界限,否則也會(huì)造成類似的錯(cuò)誤。
[1]王敬華,等.C 語(yǔ)言程序設(shè)計(jì)教程[M].北京:清華大學(xué)出版社.2006.264-380.
[2]譚浩強(qiáng).C語(yǔ)言程序設(shè)計(jì)[M].北京:清華大學(xué)出版社,1995,157-212.
[3]吳斌.C語(yǔ)言指針的教學(xué)[J].安徽職業(yè)技術(shù)學(xué)院學(xué)報(bào).2004,(9):67-69.
(責(zé)任編輯 李學(xué)斌)
TP312
A
1673-2014(2010)02-0026-03
2010—04—02
李 瑛(1973— ),女,河北保定人,碩士,講師,主要從事計(jì)算機(jī)人工智能研究。