廈門(mén)理工學(xué)院11級(jí)C語(yǔ)言-第07章-指針_第1頁(yè)
廈門(mén)理工學(xué)院11級(jí)C語(yǔ)言-第07章-指針_第2頁(yè)
廈門(mén)理工學(xué)院11級(jí)C語(yǔ)言-第07章-指針_第3頁(yè)
廈門(mén)理工學(xué)院11級(jí)C語(yǔ)言-第07章-指針_第4頁(yè)
廈門(mén)理工學(xué)院11級(jí)C語(yǔ)言-第07章-指針_第5頁(yè)
已閱讀5頁(yè),還剩146頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第七章指針指針在C語(yǔ)言中占有重要的地位,是最具有特色的語(yǔ)言成分,是C語(yǔ)言的精華。正確而靈活地使用它,可以有效地表示復(fù)雜的數(shù)據(jù)結(jié)構(gòu);能動(dòng)態(tài)分配內(nèi)存;直接處理內(nèi)存地址;對(duì)內(nèi)存中各種不同的數(shù)據(jù)結(jié)構(gòu)進(jìn)行快速處理,也為函數(shù)間各類(lèi)數(shù)據(jù)的傳遞提供了簡(jiǎn)捷便利的方法。使用指針,可以編制出簡(jiǎn)潔明快、功能強(qiáng)和質(zhì)量高的程序。但指針也是最有風(fēng)險(xiǎn)的。譬如,未初始化的指針可能造成系統(tǒng)的錯(cuò)誤。也許夸張了,但指針確實(shí)很容易被誤用,使得系統(tǒng)造成難以發(fā)現(xiàn)的錯(cuò)誤。造成程序“掛死”的大部分原因都是由于錯(cuò)誤地使用指針或數(shù)組越界所造成的。

C程序設(shè)計(jì)中使用指針可以: 使程序簡(jiǎn)潔、緊湊、高效有效地表示復(fù)雜的數(shù)據(jù)結(jié)構(gòu)動(dòng)態(tài)分配內(nèi)存得到多于一個(gè)的函數(shù)返回值7.1指針的基本概念1.內(nèi)存地址在計(jì)算機(jī)硬件系統(tǒng)的內(nèi)存儲(chǔ)器中,擁有大量的存儲(chǔ)單元(以字節(jié)為單位)。為了便于管理,每個(gè)存儲(chǔ)單元都有惟一的編號(hào),這個(gè)編號(hào)就是存儲(chǔ)單元的“地址”。例如,對(duì)16位機(jī),DOS環(huán)境下的應(yīng)用程序,其代碼段、數(shù)據(jù)段和堆棧段放在位于內(nèi)存地址0x0000~0xffff之間的640KB常規(guī)內(nèi)存中。也就是說(shuō),程序中的某一變量,對(duì)應(yīng)0x0000~0xfff范圍內(nèi)的某些存儲(chǔ)單元。注:TC只能開(kāi)發(fā)DOS下的16位命令行方式的應(yīng)用程序。2.變量的存儲(chǔ)回顧一下變量的定義:“變量代表內(nèi)存中具有特定屬性的一個(gè)存儲(chǔ)單元,它用來(lái)存放數(shù)據(jù),也就是變量的值,在程序運(yùn)行期間,這些值是可以改變的,一個(gè)變量應(yīng)該有一個(gè)名字,以便被引用?!毕旅嬉砸粋€(gè)簡(jiǎn)單的程序討論一下變量在內(nèi)存中的存儲(chǔ)情況。關(guān)鍵字:變量名、存放變量的內(nèi)存單元格、內(nèi)存單元格的地址、變量值7.1.1預(yù)備知識(shí)下面以一個(gè)簡(jiǎn)單的程序討論一下變量在內(nèi)存中的存儲(chǔ)情況。關(guān)鍵字:變量名、存放變量的內(nèi)存單元格、內(nèi)存單元格的地址、變量值main(){

inta=1;floatb=2;

intc[2]={5,6};chard=’d’;}一個(gè)程序片段變量a變量b變量d數(shù)組c500000000H00000001H00000002H00000003H00000004H00000005H100000006H00000007H00000008H00000009H…...60000000AH00000009H‘d’2變量名a變量的地址存放變量的內(nèi)存單元00000000H00000001H1變量的值圖7-2變量a的局部示意圖注:變量的地址是二進(jìn)制的,為了便于書(shū)寫(xiě)而在這里寫(xiě)成對(duì)應(yīng)的十六進(jìn)制形式。等讀者熟悉后在以后的章節(jié)中則會(huì)直接用十進(jìn)制來(lái)書(shū)寫(xiě),以便于閱讀。程序中分別定義了4個(gè)變量:int變量a,float變量b,int數(shù)組c,char變量d,它們?cè)趦?nèi)存中分別占據(jù)2個(gè)、4個(gè)、4個(gè)、1個(gè)字節(jié)。其中變量a的首地址是00000000H(這是假設(shè),實(shí)際情況中程序定義的變量并不是從內(nèi)存的0字節(jié)開(kāi)始存放的),則變量b的首地址是00000002H,數(shù)組c的首地址是00000006H,變量d的首地址是0000000AH。首地址就簡(jiǎn)稱(chēng)為變量的地址。要訪問(wèn)變量首先就要知道變量的地址,可是通過(guò)數(shù)字形式的地址值訪問(wèn)變量,顯然是不方便的(正如使用URL網(wǎng)址比IP地址要方便):不便于書(shū)寫(xiě)和記憶,而且數(shù)字本身沒(méi)有什么具體的字面意義。需要了解硬件細(xì)節(jié)。比如當(dāng)前哪些內(nèi)存空間是空閑的等等。這就失去了高級(jí)語(yǔ)言容易使用、接近人類(lèi)語(yǔ)言的優(yōu)點(diǎn)。好在C語(yǔ)言提供了變量名,程序員通過(guò)變量名來(lái)訪問(wèn)變量,不需要知道變量的存儲(chǔ)單元是如何開(kāi)辟在內(nèi)存的空閑區(qū)的,也不需要關(guān)心變量的實(shí)際存放地址。變量名和變量的地址之間由編譯器和操作系統(tǒng)進(jìn)行聯(lián)系和轉(zhuǎn)換(最終當(dāng)然還是要通過(guò)地址對(duì)變量進(jìn)行訪問(wèn)),這個(gè)轉(zhuǎn)換過(guò)程對(duì)程序員來(lái)說(shuō)是透明的。這樣做顯然是有好處的:變量名比地址好記而且可以表文達(dá)意,提高了程序的書(shū)寫(xiě)性和可讀性;普通程序員可以把更多的精力放在程序的邏輯實(shí)現(xiàn)上而不需要過(guò)分關(guān)注計(jì)算機(jī)硬件系統(tǒng)的有關(guān)細(xì)節(jié)。這些也正是高級(jí)語(yǔ)言的優(yōu)點(diǎn)之表現(xiàn)。注:常量是沒(méi)有地址的。(以后通過(guò)對(duì)匯編語(yǔ)言的學(xué)習(xí),我們可以了解到常量的存儲(chǔ))7.1.2指針就是地址它們具有雙重含義什么是指針呢?指針其實(shí)就是地址!既然變量名比變量地址使用起來(lái)方便,那么為什么還要引入指針呢?這是因?yàn)橹羔樋梢越o我們的程序帶來(lái)意想不到的靈活度,隨著本章的深入學(xué)習(xí),您一定會(huì)體會(huì)到這句話(huà)的!“指針就是地址”,因此對(duì)指針的認(rèn)識(shí)要建立在對(duì)地址的深刻理解之上。地址有兩個(gè)方面的含義。地址值(也就是內(nèi)存單元的編址)。是什么類(lèi)型的數(shù)據(jù)的地址。這就存在著一個(gè)跨度也就是存儲(chǔ)空間大小的問(wèn)題。(我們已經(jīng)知道,不同的數(shù)據(jù)類(lèi)型其占據(jù)內(nèi)存空間的大小是不同的。比如對(duì)于一個(gè)int變量的地址,應(yīng)該是內(nèi)存中某2個(gè)連續(xù)字節(jié)單元的首地址;如果是一個(gè)float變量的地址,那么該指針應(yīng)該是內(nèi)存中某4個(gè)連續(xù)字節(jié)單元的首地址)。7.1.3指針其名明白指針就是地址,這一點(diǎn)十分重要。多數(shù)情況下,這個(gè)地址是內(nèi)存中另一個(gè)變量的位置。如果一個(gè)變量包含了另一個(gè)變量的地址,那么第1個(gè)變量就是個(gè)指針變量而且說(shuō)它是“指向”第2個(gè)變量的,“指針”由此而得其名。例如,如果在地址為1000的變量指向地址為1004的變量,那么也就是說(shuō)地址為1000的這個(gè)變量的值是1004。為什么要表達(dá)為“指向”呢?下一節(jié)中將會(huì)看到如果變量p的值是變量a的地址,則可以利用變量p來(lái)訪問(wèn)和操作變量a(其實(shí)這是很自然的事情,有了某變量的地址當(dāng)然就可以訪問(wèn)該變量)。所以這樣的變量p和a之間是有種聯(lián)系的,這種聯(lián)系就被表達(dá)為“指向”。圖7-3解釋了這一點(diǎn),它僅僅用來(lái)對(duì)地址進(jìn)行偏移。100410001001100210031004圖7-3一個(gè)變量指向另一個(gè)變量?jī)?nèi)存單元內(nèi)存地址7.1.4變量的指針與指針變量1.變量的指針一個(gè)變量x的地址就是該變量的指針,記作&x,即在變量名前加上取地址運(yùn)算符“&”。例如,變量x的地址是0x2000,我們就說(shuō)x的指針是0x2000。顯然每個(gè)變量的地址或說(shuō)指針都是客觀存在的,而且是個(gè)常量。2.指針變量大家都知道整型變量就是存放整數(shù)的變量,同理,專(zhuān)門(mén)用來(lái)存放地址的變量稱(chēng)為指針變量。當(dāng)指針變量中存放著某一個(gè)變量的地址時(shí),就稱(chēng)這個(gè)指針變量指向那一個(gè)變量。由于地址或指針是常量,因此當(dāng)我們需要對(duì)地址進(jìn)行操作的時(shí)候一般要用指針變量來(lái)保存該地址再做處理。3.指針變量與它所指向的變量的關(guān)系指針變量和一般變量既有聯(lián)系又與區(qū)別。指針變量也是變量,具有變量的特征,在內(nèi)存中也占用一定的存儲(chǔ)單元,也有“地址”和“值”的概念。但指針變量的“值”不同于一般變量的“值”,指針變量的“值”是另一實(shí)體(變量、數(shù)組或函數(shù)等)的地址。指針變量px與它所指向的整型變量x的關(guān)系,用指針運(yùn)算符“*”表示為:“*px等價(jià)于變量x”

因此,下面四條語(yǔ)句的作用相同,都是將100賦給變量x:x=100;/*將100直接賦給變量x*/*px=100;/*將100間接賦給變量x*/*(&x)=100;/*將100間接賦給變量x*/*((int*)100h)=100/*將100間接賦給變量x*/(假設(shè)x的地址為100h)請(qǐng)考慮第4種訪問(wèn)方式:這里之所以要進(jìn)行類(lèi)型轉(zhuǎn)換是因?yàn)?00h只是個(gè)地址值,并沒(méi)有關(guān)于其代表存儲(chǔ)空間大小的描述,不具備我們前面討論的地址的雙重含義。為了把100放到以100h為起始地址的連續(xù)兩個(gè)字節(jié)中,因此需要進(jìn)行相應(yīng)的類(lèi)型轉(zhuǎn)換,使得100h具有跨度上的含義后它作為一個(gè)地址意義才完整,也才能正確地利用它把100賦給變量x。4.指針變量的長(zhǎng)度指針變量的長(zhǎng)度可以是2個(gè)字節(jié)或4個(gè)字節(jié),這取決于引用者和被引用者之間在內(nèi)存的距離,通常由系統(tǒng)自動(dòng)決定,程序員不必理會(huì)。(下述內(nèi)容在學(xué)習(xí)匯編語(yǔ)言后更好理解,目前可暫不關(guān)注)編譯系統(tǒng)根據(jù)設(shè)定的內(nèi)存模式來(lái)安排代碼段和數(shù)據(jù)段,由此確定指針變量的長(zhǎng)度。內(nèi)存模式取決于代碼段和數(shù)據(jù)段的長(zhǎng)度,早期的C系統(tǒng)將內(nèi)存模式分為六種,如表7-1所示。表7-1內(nèi)存模式模式代碼段長(zhǎng)度數(shù)據(jù)段及靜態(tài)數(shù)據(jù)長(zhǎng)度微型模式(TinyModel)代碼段、數(shù)據(jù)段和數(shù)據(jù)組的總長(zhǎng)度<64KB代碼段、數(shù)據(jù)段和數(shù)據(jù)組的總長(zhǎng)度<64KB小型模式(TinyModel)<64KB<64KB中型模式(TinyModel)無(wú)限制

<64KB緊湊型模式(TinyModel)<64KB無(wú)限制,但靜態(tài)數(shù)據(jù)<64KB大型模式(TinyModel)無(wú)限制無(wú)限制,但靜態(tài)數(shù)據(jù)<64KB巨型模式(TinyModel)無(wú)限制無(wú)限制當(dāng)數(shù)據(jù)段(或代碼段)的長(zhǎng)度在64KB以?xún)?nèi)時(shí),其地址長(zhǎng)度不超過(guò)16位,因此保存這樣地址的指針變量只需要2個(gè)字節(jié)即可。當(dāng)數(shù)據(jù)段(或代碼段)的長(zhǎng)度超過(guò)64kB時(shí),其地址長(zhǎng)度超過(guò)16位,要保存這樣的地址,指針變量需要4個(gè)字節(jié)。通常指針變量的長(zhǎng)度由系統(tǒng)自動(dòng)決定,程序員只需根據(jù)程序中代碼和數(shù)據(jù)量的多少選用適當(dāng)?shù)膬?nèi)存模式即可,其他事都由系統(tǒng)自行處理。請(qǐng)注意:指針變量中不僅僅只能存放變量的地址,例如還能存放函數(shù)的入口地址等等,這里暫不作討論。前面提到,常量是沒(méi)有地址的。因此變量才有(指向它的)指針/指針變量,亦即指針/指針變量只能指向變量。因此“(指向)某類(lèi)型的指針/指針變量”就是指“(指向)某類(lèi)型的變量的指針/指針變量”。比如:“(指向)整型的指針/指針變量”就是指“(指向)整型變量的指針/指針變量”等等,以此類(lèi)推。7.2指針變量的定義和賦值7.2.1指針變量的定義指針也是C的一種數(shù)據(jù)類(lèi)型,指針變量的定義形式和前面學(xué)過(guò)的其他數(shù)據(jù)類(lèi)型的變量之定義沒(méi)有什么大的區(qū)別。

C數(shù)據(jù)類(lèi)型的變量的一般定義形式類(lèi)型名關(guān)鍵字變量名標(biāo)識(shí)符[=初始值];注:[]中的是可選項(xiàng)定義指針變量的一般形式為:類(lèi)型名關(guān)鍵字*變量名標(biāo)識(shí)符[=初始值];注意為了與普通變量區(qū)別開(kāi)來(lái),在變量名前加”*”來(lái)說(shuō)明它是指針變量?!?”并不屬于指針變量名標(biāo)識(shí)符的部分?!邦?lèi)型名關(guān)鍵字”是表示該指針是指向什么類(lèi)型的變量的。既然指針有兩個(gè)方面的含義,那么在指針變量的定義中是怎么體現(xiàn)這兩方面的含義呢?例如:

inta=1;/*定義了一個(gè)變量a,那么變量a在內(nèi)存的空閑區(qū)占據(jù)了2個(gè)字節(jié)的空間*/

int*p;/*(注意指針變量是p而不是*P)定義了一個(gè)指針變量,這個(gè)指針變量是指向int變量的(即是用來(lái)盛放int變量的地址的),所以這個(gè)指針變量中盛放的地址一定是內(nèi)存中某2個(gè)連續(xù)的字節(jié)單元空間的首地址。這就把跨度含義賦予了該指針變量。*/p=&a;/*把int變量a的首地址賦予該指針變量,對(duì)指針的另一個(gè)含義“地址值”進(jìn)行補(bǔ)充。*/

當(dāng)然了,上面的三個(gè)語(yǔ)句也可以簡(jiǎn)化為

inta=1;

int*p=&a;

這樣是在定義指針變量的同時(shí)就把地址值給了指針變量,效果是一樣的。通過(guò)圖7-4可以更好地理解什么是指針變量:圖7-4變量在內(nèi)存中的存儲(chǔ)分配程序片段inta=10;……..……..(定義別的變量)int*p;p=&a;········00000000H00000001H········00000006H00000005H········00000065H········40000000H00000064H················a························P················1000000005H································變量名變量地址變量值在上圖中可以看到,變量p在內(nèi)存中占據(jù)2個(gè)字節(jié)。只不過(guò)它里面存放的是變量a的地址,這種關(guān)系表現(xiàn)為圖7-5中的箭頭,它表示p“指向”a。00000005H1指針變量p整型變量a,它的地址是00000005H圖7-5指針變量p指向變量a7.2.2指針變量的賦值指針變量可以通過(guò)不同的方法獲得一個(gè)地址值。不管是用什么方法,說(shuō)白了就是用地址表達(dá)式給指針變量賦值,這個(gè)地址表達(dá)式可以是常量,可以是變量,也可以是函數(shù)的返回值。注意:指針具有雙重含義,所以對(duì)指針變量賦值的地址表達(dá)式應(yīng)該和定義指針變量的時(shí)候賦予的兩方面的含義相符合,否則就會(huì)出錯(cuò)。1.通過(guò)取地址運(yùn)算符“&”賦值地址運(yùn)算符號(hào)“&”是單目運(yùn)算符,運(yùn)算對(duì)象放在地址運(yùn)算符“&”的右邊,用于求出運(yùn)算對(duì)象的地址。通過(guò)地址運(yùn)算“&”可以把一個(gè)變量的地址賦給指針變量。

floatf,*p;p=&f;

執(zhí)行后把變量f的地址賦給指針變量p,指針變量p就指向了變量f。2.指針變量的初始化可以在定義變量時(shí)給指針變量賦初值,如floatf,*p=&f;,則把變量f的地址賦值給指針變量p,此語(yǔ)句相當(dāng)于floatf,*p;p=&f;這兩條語(yǔ)句。3.通過(guò)其他指針變量賦值可以通過(guò)賦值運(yùn)算符,把一個(gè)指針變量的地址值賦給另一個(gè)指針變量,這樣兩個(gè)指針變量均指向同一變量。例如,有如下程序段:

inti,*p1=&i,*p2;p2=p1;

執(zhí)行后指針變量p1與p2都指向整型變量i。注意,當(dāng)把一個(gè)指針變量的地址值賦給另一個(gè)指針變量時(shí),賦值號(hào)兩邊指針變量所指的數(shù)據(jù)類(lèi)型必須相同。例如:

inti,*pi=&i;float*pf;

則語(yǔ)句pf=pi;是非法的,因?yàn)閜f只能指向?qū)嵭妥兞?,而不能指向整型變量?.用NULL給指針變量賦空值除了給指針變量賦地址值外,還可以給指針變量賦空值,如:

p=NULL;

NULL是在stdio.h頭文件中定義的預(yù)定義標(biāo)識(shí)符,因此在使用NULL的時(shí)候,應(yīng)該在程序中加上文件包含“stdio.h”。在stdio.h頭文件中NULL被定義成符號(hào)常量,與整數(shù)0對(duì)應(yīng)。執(zhí)行以上的賦值語(yǔ)句后,p為空指針(和指向void空類(lèi)型的指針是不同的),在C語(yǔ)言中當(dāng)指針值為NULL時(shí),指針不指向任何有效數(shù)據(jù),因此在程序中為了防止錯(cuò)誤地使用指針來(lái)存取數(shù)據(jù),常常在指針未使用之前,先賦初值為NULL。由于NULL與整數(shù)0想對(duì)應(yīng),所以下面三條語(yǔ)句等價(jià):

p=NULL;或p=0;或p=’\0’;但通常都使用p=NULL;的形式,因?yàn)檫@條語(yǔ)句的可讀性好。NULL可以賦值給指向任何類(lèi)型的指針變量。5.通過(guò)調(diào)用標(biāo)準(zhǔn)庫(kù)函數(shù)賦值可以調(diào)用庫(kù)函數(shù)malloc、calloc等在內(nèi)存中開(kāi)辟動(dòng)態(tài)存儲(chǔ)單元,并把所開(kāi)辟的動(dòng)態(tài)存儲(chǔ)單元的地址賦給指針變量。由于這兩個(gè)函數(shù)返回的是“void*”無(wú)類(lèi)型指針類(lèi)型,因此將它們的返回值賦值給指針變量的時(shí)候要進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換。注:這個(gè)動(dòng)態(tài)存儲(chǔ)單元到底如何“動(dòng)態(tài)”以及malloc、calloc函數(shù)的用法將在鏈表部分進(jìn)行講述。7.2.3void指針前面講過(guò)指針有兩個(gè)方面的含義,兩個(gè)含義必須同時(shí)存在指針才可以正常工作。ANSIC中有void類(lèi)型,如果指針指向void類(lèi)型,那么該指針就不指向任何實(shí)際的數(shù)據(jù)類(lèi)型了。因此在實(shí)際需要的時(shí)候通過(guò)給它補(bǔ)充該含義,可以使它可以重新指向?qū)嶋H的數(shù)據(jù)類(lèi)型。

void*p;這個(gè)p是個(gè)指針變量,但是并不明確它要指向什么類(lèi)型的變量。

inta=1;p=(int*)&a;現(xiàn)在希望p存放int變量a的地址,也就是說(shuō)想用p指向int變量a,那么把地址值和p要跨度兩個(gè)方面的含義都賦予指針才完整。那么這樣的void指針究竟有什么利用價(jià)值呢?它不指向任何實(shí)際的數(shù)據(jù)類(lèi)型,也就是說(shuō)它蛻化掉了指針“跨度”的含義,也就是說(shuō)它指向何種類(lèi)型的數(shù)據(jù)類(lèi)型是不明確的,但是可以把它想象成指向一個(gè)抽象的數(shù)據(jù)類(lèi)型。在將它的值賦給另一個(gè)指針變量的時(shí)候,要進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換使之適合于被賦值的變量之?dāng)?shù)據(jù)類(lèi)型。ANSIC標(biāo)準(zhǔn)規(guī)定用動(dòng)態(tài)存儲(chǔ)分配函數(shù)時(shí)返回void指針。這部分的內(nèi)容在講述動(dòng)態(tài)建立鏈表的時(shí)候?qū)⑸钊雽W(xué)習(xí),這里暫不作討論。抽象數(shù)據(jù)類(lèi)型在《數(shù)據(jù)結(jié)構(gòu)》中也是一種重要的概念。7.3指針變量的使用指針變量的使用包括通過(guò)指針變量訪問(wèn)變量和移動(dòng)指針。7.3.1指針運(yùn)算符指針變量有兩種運(yùn)算:“&”、“*”。

1.取地址運(yùn)算符“&”

這個(gè)運(yùn)算符前面已經(jīng)用到。例如賦值語(yǔ)句:

px=&x;

就是通過(guò)取地址運(yùn)算符“&”,把變量x的地址賦給指針變量的,也就是使px指向x。2.指針運(yùn)算符“*”(根據(jù)地址取變量值)對(duì)于圖7-4中所示的程序。px指向x后,就可以通過(guò)px間接訪問(wèn)它所指向的變量x了。*px就等價(jià)于x,所以,以下兩條賦值語(yǔ)句:*px=10;x=10;

是等價(jià)的,都是將10賦給x。同樣,下兩條語(yǔ)句:

printf(“x=%d”,x);

printf(“x=%d”,*px);

直接和間接方式輸出變量x的值,因此,輸出結(jié)果都是10。3.指針的算術(shù)運(yùn)算指針可以加上或減去一個(gè)整數(shù)(常量或變量),達(dá)到改變地址值也就是對(duì)指針進(jìn)行移動(dòng)的目的。例如:

y是個(gè)整數(shù),p是一個(gè)指針變量,如果它所指向的數(shù)據(jù)類(lèi)型在內(nèi)存中占據(jù)x個(gè)字節(jié)的存儲(chǔ)空間,則p+y表示在p的地址值基礎(chǔ)上前進(jìn)x*y個(gè)字節(jié)。減法運(yùn)算原理相似,是在p的地址值基礎(chǔ)上后退。7.3.2變量的存取方式(1)直接訪問(wèn)C語(yǔ)言作為一種高級(jí)語(yǔ)言為了方便程序員使用,使用變量名訪問(wèn)變量,用變量名對(duì)變量進(jìn)行訪問(wèn)叫做直接訪問(wèn)。

(2)間接訪問(wèn)變量名本質(zhì)上還是要轉(zhuǎn)換為地址而后訪問(wèn)變量。指針變量的值就是地址,所以可以通過(guò)指針變量來(lái)“間接”訪問(wèn)它所指向的變量。用指針變量對(duì)其所指向的變量進(jìn)行訪問(wèn)的方式叫做間接訪問(wèn)。7.3.3停下來(lái)思考一下論語(yǔ)有云:“學(xué)而不思則罔,思而不學(xué)則殆”。一路學(xué)到這里可能有多人會(huì)問(wèn),前面說(shuō)指針好,那指針到底好在哪里呢?下面我們就停下來(lái)思考一下,對(duì)前面的知識(shí)梳理一下,對(duì)后邊的內(nèi)容也做一個(gè)交代,以期起到承上啟下的作用。首先,在7.1.1預(yù)備知識(shí)中已經(jīng)分析過(guò)變量的直接訪問(wèn)方式的諸多優(yōu)點(diǎn),那么通過(guò)指針變量來(lái)實(shí)現(xiàn)對(duì)變量的間接訪問(wèn)不就是畫(huà)蛇添足了嗎?其實(shí)我們?cè)诤瘮?shù)一章中學(xué)習(xí)過(guò)變量的性質(zhì)后很容易回答這個(gè)問(wèn)題。下面我們先來(lái)看一個(gè)以前考慮過(guò)的實(shí)際問(wèn)題:背景:王老師任教計(jì)算機(jī)a班和b班,a班有一個(gè)同學(xué)叫做小明,而b班沒(méi)有。今天王老師去b班上課,在b班的課堂上王老師突然想找小明。(這在C程序中好比在一個(gè)函數(shù)中卻想訪問(wèn)或操縱另一個(gè)函數(shù)中的局部變量)思考:王老師是不能用“小明”這個(gè)名字來(lái)訪問(wèn)該同學(xué)的,這是因?yàn)樾∶鬟@個(gè)名字根本不存在于b班。這就好比小明是定義在a班的范圍內(nèi)的一個(gè)局部變量,而現(xiàn)在是在b班的范圍內(nèi),當(dāng)然不能通過(guò)小王來(lái)訪問(wèn)所對(duì)應(yīng)的同學(xué)了,因?yàn)槲覀円呀?jīng)知道局部變量在此函數(shù)之外是無(wú)效或說(shuō)是不可見(jiàn)的。更術(shù)語(yǔ)地講可以說(shuō)小明這個(gè)名字的“名字空間”是a班,在a班的范圍內(nèi),王老師引用小明,會(huì)找到對(duì)應(yīng)的同學(xué),在b班則不行。所以王老師對(duì)班長(zhǎng)說(shuō)“幫我把小明同學(xué)找來(lái)”,是達(dá)不到目的的。可以想到全校的同學(xué)都有唯一的學(xué)號(hào),如果王老師此時(shí)用小明的學(xué)號(hào),是可以訪問(wèn)小明的,比如王老師對(duì)班長(zhǎng)說(shuō)“幫我把9941083同學(xué)找來(lái)一下”是可以達(dá)到訪問(wèn)小明的目的的。因?yàn)樵搶W(xué)號(hào)客觀存在而且每個(gè)人都不一樣。通過(guò)指針變量對(duì)它所指向的變量進(jìn)行間接訪問(wèn)事實(shí)上和王老師通過(guò)學(xué)號(hào)達(dá)到訪問(wèn)另一個(gè)范圍內(nèi)的同學(xué)之情況是相似的。換句話(huà)說(shuō),利用指針變量對(duì)變量進(jìn)行間接訪問(wèn),可以跨越名字空間訪問(wèn)變量。這聽(tīng)起來(lái)好象顯的很神秘,其實(shí)是很自然的事情,因?yàn)樵趦?nèi)存中變量的地址是唯一的,正如學(xué)生的學(xué)號(hào)一樣,只要該變量仍然客觀存在著就行。在下一節(jié)我們就可以看到這個(gè)特性能給我們帶來(lái)什么便捷。當(dāng)然了,指針的靈活之處還遠(yuǎn)不止于此,我們將會(huì)慢慢學(xué)來(lái)。到現(xiàn)在您還跟得上進(jìn)度嗎?7.3.4指針變量作為函數(shù)參數(shù)大家已經(jīng)知道,C語(yǔ)言中函數(shù)參數(shù)的傳遞方式是“值傳遞”。指針變量作為函數(shù)的實(shí)參時(shí),由于指針變量的值實(shí)際上是它所指向的變量的地址,所以傳遞的是指針?biāo)赶虻淖兞康牡刂?,與此相對(duì)應(yīng)函數(shù)的形參也應(yīng)是指針變量。前面的程序例6-18中的change函數(shù)并不能交換a和b的值,下面我們通過(guò)一個(gè)程序看看使用指針變量來(lái)間接訪問(wèn)變量能帶來(lái)什么好處。例7-1編制函數(shù)change,交換兩個(gè)變量的值。voidchange(int*m,int*n){

inttemp;temp=*m;*m=*n;*n=temp;}voidmain(){

inta=1,b=2;

change(&a,&b);

printf("a=%d,b=%d",a,b);}圖7-6程序的內(nèi)存示意圖10010210010221釋放maab12100102n204206temp隨機(jī)值208b100102mn100102204206temp1208指向值地址temp=*m;*m=*n;*n=temp;ab2100102mn204206temp2208釋放釋放change返回調(diào)用change(&a,&b);inta=1,b=2;1運(yùn)行后觀察顯示結(jié)果可以發(fā)現(xiàn)變量a和b被成功地交換了。圖7.6展示了程序運(yùn)行過(guò)程中各個(gè)變量存儲(chǔ)單元中的變化。其實(shí)原因很簡(jiǎn)單,因?yàn)樵诔绦?_3.c中對(duì)變量直接訪問(wèn),main函數(shù)中的a和b與change函數(shù)中的m和n只不過(guò)值相同罷了,除了值的單向傳遞他們之間沒(méi)有任何關(guān)系,因此對(duì)m和n的改變不能反映在a和b上。而程序7_1.c中,變量m和n中存放的是變量a和b的地址,它們有“指向”的關(guān)系,那么通過(guò)*m和*n就找到并可以訪問(wèn)變量a和b了。從宏觀上來(lái)看,變量a、b是定義在main函數(shù)中的局部變量,只有在main函數(shù)中可以訪問(wèn)它們,在change函數(shù)中則不能通過(guò)變量名a、b達(dá)到訪問(wèn)它們的目的,正如我們學(xué)過(guò)的變量之有效性范圍??墒莔ain函數(shù)在調(diào)用change函數(shù)的時(shí)候把變量a、b在內(nèi)存中的地址以函數(shù)實(shí)參的形式“告訴”給了change函數(shù),因此在change函數(shù)中就可以通過(guò)指針變量來(lái)“指向”變量a和b從而達(dá)到訪問(wèn)它們的目的了。7.4指針與數(shù)組

C語(yǔ)言中指針與數(shù)組有著極為密切的聯(lián)系。引用數(shù)組元素可以用下標(biāo)法,也可以用指針?lè)?。兩者相比而言,下?biāo)法易于理解,適合于初學(xué)者;而指針表示法有利于提高程序的執(zhí)行效率。7.4.1數(shù)組和數(shù)組元素的指針數(shù)組的指針是指數(shù)組在內(nèi)存中的地址,數(shù)組元素的指針是數(shù)組元素在內(nèi)存中的地址。我們已經(jīng)知道數(shù)組名就是數(shù)組的起始地址,其實(shí)C語(yǔ)言規(guī)定它是下標(biāo)為0的元素的起始地址,而不是數(shù)組的指針。盡管如果僅從首地址值的角度考慮,數(shù)組名、數(shù)組的指針,以及下標(biāo)為0的數(shù)組元素的指針都相同。但是如果考慮到指針的完整定義,則其實(shí)數(shù)組名僅僅是下標(biāo)為0的元素的指針。另外,請(qǐng)注意它們都是常量。1.數(shù)組元素的指針例如,intdata[6];

則C語(yǔ)言規(guī)定:數(shù)組名data是指針常量,它代表的是數(shù)組第一個(gè)元素data[0]的指針。前一節(jié)我們已經(jīng)討論過(guò)指針的算術(shù)運(yùn)算,現(xiàn)在容易知道data+i其實(shí)也就是data[i]的首地址(i=0,1,2,···,5),即data+i與&data[i]等價(jià)。與簡(jiǎn)單變量類(lèi)似,數(shù)組元素data[i]的首地址&data[i]就稱(chēng)為data[i]的指針。所以data+i也是data[i]的指針,簡(jiǎn)稱(chēng)為data+i指向data[i]。因而引用數(shù)組元素時(shí),可用*data、*(data+1)、····*(data+5)的方式,如圖7-7(a)所示。圖7-7用指針引用數(shù)組元素datadata+1data+2data+3data+4data+5data[0]data[1]data[2]data[3]data[4]data[5](a)pp+1p+2p+3p+4p+5data[0]data[1]data[2]data[3]data[4]data[5](b)所以,以下兩個(gè)循環(huán)輸出語(yǔ)句完全等價(jià):

for(i=0;i<6;i++)printf(“%4d”,data[i]);

for(i=0;i<6;i++)printf(“%4d”,*(data+i));2.數(shù)組的指針那么我們?cè)趺传@得數(shù)組的指針呢?其實(shí)在下標(biāo)為0的數(shù)組元素的指針上(也就是數(shù)組名)作個(gè)類(lèi)型轉(zhuǎn)換即可。例如對(duì)上述的數(shù)組intdata[6]來(lái)說(shuō),((int*)[6])data就是數(shù)組data的指針。一般來(lái)說(shuō)在二維和多維數(shù)組中,我們經(jīng)常會(huì)用到數(shù)組的指針和指向數(shù)組的指針變量,對(duì)于它們來(lái)說(shuō),可能其值和某數(shù)組元素的首地址相等,但是其含義是不同的,例如((int*)[6])data+1則表示指針前進(jìn)6*2=12個(gè)字節(jié),而不是前進(jìn)2個(gè)字節(jié),很明顯它和數(shù)組元素的指針在跨度含義上是不同的。分析:對(duì)于強(qiáng)制類(lèi)型轉(zhuǎn)換((int*)[6])data,考慮到運(yùn)算符的優(yōu)先級(jí)和結(jié)合性,容易知道data被轉(zhuǎn)換成了指向擁有6個(gè)元素的整型數(shù)組的指針。7.4.2指向數(shù)組和數(shù)組元素的指針變量1.指向數(shù)組元素的指針變量指向某數(shù)組元素的指針變量就是指向該數(shù)組元素的指針變量。定義一個(gè)指向數(shù)組元素的指針變量的方法,與以前介紹的指向變量的指針變量相同。而且由于數(shù)組的元素就是某種類(lèi)型的變量,因此指向數(shù)組元素的指針變量在定義形式上就是指向該類(lèi)型變量的指針變量,只不過(guò)需要利用數(shù)組元素的地址對(duì)其賦值罷了。例如:intdata[6];/*定義data為包含6個(gè)整型數(shù)據(jù)的數(shù)組*/int*p;/*定義p為指向整型變量的指針變量*/則語(yǔ)句:p=&data[0];(或p=data;)就把data[0]元素的地址賦給指針變量p,即p指向data數(shù)組的第0號(hào)元素(也可說(shuō)p指向data數(shù)組)。同樣,語(yǔ)句p=&data[i];就使p指向data數(shù)組的第i號(hào)元素。如果p的初值為&data[0],則p+i就是data[i]的地址&data[i](i=0,1,2,···,5)。如果p指向數(shù)組中的一個(gè)元素,則p+1就指向同一數(shù)組的下一個(gè)元素。p+1所代表的地址實(shí)際上是p+1×d,d是一個(gè)數(shù)組元素所占字節(jié)數(shù)(對(duì)整數(shù)型,d=2;對(duì)實(shí)型,d=4;對(duì)字符型,d=1)如圖7-7(b)所示。2.指向一維數(shù)組的指針變量指向數(shù)組的指針變量要略顯復(fù)雜一些,但如果我們把握住指針的本質(zhì),很容易想到它具有的兩個(gè)屬性分別是:其值是數(shù)組的首地址,亦即下標(biāo)為0的數(shù)組元素的地址。它是指向數(shù)組的,而不是數(shù)組元素。因此它的跨度是整個(gè)數(shù)組而不是一個(gè)數(shù)組元素。下面定義一個(gè)指向數(shù)組的指針變量。

int(*p)[6]

intdata[5][6];p=data[2];p++應(yīng)該記住,此時(shí)p只能指向一個(gè)包含6個(gè)元素的一維數(shù)組。p的值就是該一維數(shù)組的起始地址(亦即下標(biāo)為0的數(shù)組元素的地址),p不能指向一維數(shù)組中的某一元素。指向一維數(shù)組的指針變量一般用于對(duì)二維數(shù)組或多維數(shù)組的操作,請(qǐng)務(wù)必主意指針變量的類(lèi)型。3.指向數(shù)組元素的指針變量,在使用中應(yīng)注意的問(wèn)題(1)指針變量可以通過(guò)本身值的改變(如p++)來(lái)指向數(shù)組中的不同元素。但是數(shù)組名是地址常量不能改變本身的值,如果寫(xiě)data++則是錯(cuò)誤的;(2)應(yīng)注意下面的幾種指針運(yùn)算形式:*p++等價(jià)于*(p++),作用是先得到p所指向的變量的值(即*p),然后再使p加1。*(p++)與*(++p)不同,前者是先取得*p的值,后使p加1;后者是先使p加1,再取*p的值。若p初值為&data[0],輸出*(p++)時(shí),得data[0]的值,而輸出*(++p),則得到data[1]的值??偨Y(jié)以上兩點(diǎn)可知,如果p當(dāng)前指向data數(shù)組中第i個(gè)元素,則:*(p++)相當(dāng)于data[i++],先對(duì)p進(jìn)行“*”運(yùn)算,再使p自增。*(p--)相當(dāng)于data[i--],先對(duì)p進(jìn)行“*”運(yùn)算,再使p自減。*(++p)相當(dāng)于data[++i],先使p自增,再對(duì)p進(jìn)行“*”運(yùn)算。data[0]p1data[1]data[2]data[3]data[4]data[5]p2data數(shù)組圖7-8指針?biāo)阈g(shù)、關(guān)系運(yùn)算10021010*(--p)相當(dāng)于data[--i],先使p自減,再對(duì)p進(jìn)行“*”運(yùn)算。

(3)(*p)++表示p所指向的元素值加1,注意是元素值加1,而不是指針值加1。比如,如果p所指向的元素為data[3],且data[3]的值為9,則(*p)++表示將data[3]單元中的值加1,變成10,而p仍指向元素data[3],也就是說(shuō),p中的地址值并沒(méi)有改變。

(4)p+n和p-n:將指針從當(dāng)前位置前進(jìn)或回退n個(gè)元素,而不是n個(gè)字節(jié)。顯然,p++、p--(或++p、--p)是p+n(p-n)的特例(n=1)。

(5)p2-p1表示兩指針之間的數(shù)組元素個(gè)數(shù),而不是指針的地址之差,即圖11-8中,p2-p1為4。

(6)兩指針之間可以進(jìn)行關(guān)系運(yùn)算,如果p1指向data[i],p2指向data[j],并且i<j,則p1<p2為“真”,反之亦然,即圖11-8中,p1<p2為真。data[0]p1data[1]data[2]data[3]data[4]data[5]p2data數(shù)組圖7-8指針?biāo)阈g(shù)、關(guān)系運(yùn)算100210107.4.3數(shù)組元素的引用數(shù)組元素的引用,既可用下標(biāo)法,也可以用指針?lè)?。下?biāo)法簡(jiǎn)單直觀;而指針?lè)苁沟媚繕?biāo)程序簡(jiǎn)短(特別是采用指針變量的自增自減運(yùn)算時(shí))、運(yùn)算速度快。例如,若有如下定義

intdata[6];

int*p=data;

則指針和數(shù)組之間有如下恒等式:

data+i==&data[i]==p+i(i=0,1,···,5)

data[i]==*(data+i)==*(p+i)==p[i](i=0,1,···,5)所以,引用數(shù)組第i個(gè)元素,有以下訪問(wèn)方式:(1)下標(biāo)法數(shù)組名下標(biāo)法:data[i]指針變量下標(biāo)法:p[i](2)指針?lè)〝?shù)組名指針?lè)ǎ?(data+i)指針變量指針?lè)ǎ?(p+i)例如,下面4條語(yǔ)句的作用都是將20賦給data[5]元素。

data[5]=20;*(data+5)=20;*(p+5)=20;p[5]=20;例7-2用下標(biāo)法和指針?lè)ㄒ脭?shù)組元素。#include<stdio.h>main(){

intdata[6]={0,3,6,9,12,15};

int*p=data,i;

for(i=0;i<6;i++)

printf(i==5?"%d\n":"%d",data[i]);/*數(shù)組名下標(biāo)法*/

for(i=0;i<6;i++)

printf(i==5?"%d\n":"%d",*(data+i));/*數(shù)組名指針?lè)?/

for(i=0;i<6;i++)

printf(i==5?"%d\n":"%d",p[i]);/*指針變量下標(biāo)法*/

for(i=0;i<6;i++)

printf(i==5?"%d\n":"%d",*(p+i));/*指針變量指針?lè)?/}運(yùn)行結(jié)果為:03691215036912150369121503691215

說(shuō)明:(1)程序中用下標(biāo)法和指針?lè)ǖ乃姆N方式引用數(shù)組元素,結(jié)果完全一樣,說(shuō)明它們是完全等價(jià)的。printf()語(yǔ)句中的格式字符串是一個(gè)條件表達(dá)式,選擇兩種輸出格式之一,輸出最后一個(gè)元素時(shí)同時(shí)輸出回車(chē)換行,輸出其他元素時(shí)同時(shí)輸出空格而不是回車(chē)換行。(2)這4種方法,前面兩種的執(zhí)行效率是一樣的。編譯程序是將data[i]轉(zhuǎn)換為*(data+i)處理的;而后兩種方法比前兩種效率高,用指針變量直接指向元素,不必每次都重新計(jì)算地址,并且象p++這樣的自加操作是比較快的。7.4.4數(shù)組名作為函數(shù)參數(shù)當(dāng)數(shù)組名作為函數(shù)參數(shù)時(shí),在函數(shù)調(diào)用時(shí),實(shí)際傳遞給函數(shù)的是該數(shù)組的起始地址,即指針值。所以,實(shí)參可以是數(shù)組名或指向數(shù)組的指針變量。而被調(diào)用函數(shù)的形參,既可以說(shuō)明為數(shù)組也可以說(shuō)明為指針。由于數(shù)組名就是數(shù)組的首地址,因此,函數(shù)的實(shí)參和形參都可以使用指向數(shù)組的指針變量或數(shù)組名,于是函數(shù)實(shí)參和形參的配合上有四種等價(jià)形式,這4種等價(jià)形式在本質(zhì)上是一種,即指針變量做函數(shù)參數(shù)。(1)實(shí)參和形參都用數(shù)組名voiddata_put(int

str[],intn){inti;for(i=0;i<n;i++)printf("\n%d",str[i]);}main(){inta[6]={1,2,3,4,5,6};data_put(a,6);}(2)實(shí)參用數(shù)組名,形參用指針voiddata_put(int*str,intn){inti;for(i=0;i<n;i++)printf("\n%d",*(str+i));}main(){inta[6]={1,2,3,4,5,6};data_put(a,6);}(3)實(shí)參用指針,形參用數(shù)組名利用下標(biāo)引用指針變量所指數(shù)組元素voiddata_put(int

str[],intn){inti;for(i=0;i<n;i++)printf("\n%d",str[i]);}main(){inta[6]={1,2,3,4,5,6};Int*p=a;data_put(p,6);}(4)實(shí)參和形參都用指針voiddata_put(int*str,intn){inti;for(i=0;i<n;i++)printf("\n%d",*(str+i));}main(){inta[6]={1,2,3,4,5,6};int*p=a;data_put(p,6);}說(shuō)明當(dāng)數(shù)組名作為函數(shù)參數(shù)時(shí),形參無(wú)論用數(shù)組名還是用指針,在函數(shù)體中對(duì)數(shù)組元素的存取操作,既可以用指針?lè)ㄒ部梢杂孟聵?biāo)發(fā)。這留給讀者驗(yàn)證。7.4.5字符串的指針和指向字符串的指針變量

C語(yǔ)言中沒(méi)有字符串類(lèi)型,字符串是以字符數(shù)組的形式給出的,而數(shù)組可以用指針進(jìn)行訪問(wèn),所以,字符串也可以用指針進(jìn)行訪問(wèn)。1.字符串的表示和引用(1)用字符數(shù)組存放一個(gè)字符串#include<stdio.h>voidmain(){charstring[]="Thisisastring";/*字符數(shù)組存放字符串*/

printf("\n%s",string);/*整體引用輸出*/

printf("\n");

for(i=0;*(string+i)!='\0';i++)

printf("%c",*(string+i));/*逐個(gè)引用*/}程序的執(zhí)行結(jié)果如下:ThisisastringThisisastring(2)用字符指針指向一個(gè)字符串#include<stdio.h>voidmain(){

inti;char*p="Thisisastring";/*字符數(shù)組存放字符串*/

printf("%s\n",p);/*整體引用輸出*/

for(i=0;p[i]!='\0';i++)

printf("%c",p[i]);/*逐個(gè)引用*/

printf("\n");for(;*p!='\0';p++)printf("%c",*p);}程序的執(zhí)行結(jié)果如下:ThisisastringThisisastringThisisastring例7-3:用字符指針指向一個(gè)字符串說(shuō)明:語(yǔ)句:char*p=”Thisisastring”;等價(jià)于下面兩行:char*p;p=“Thisisastring”;

C語(yǔ)言對(duì)字符串常量是按字符數(shù)組處理的,在定義字符串常量“Thisisastring”時(shí),在內(nèi)存開(kāi)辟了一個(gè)字符數(shù)組來(lái)存放它,并把首地址賦給字符指針p,如圖7-9所示。Thisisastring\0圖7-9字符串常量在內(nèi)存中的存放p例7_4用指針?lè)椒?,求字符串長(zhǎng)度。main(){char*p,str[80];

intn;

printf("輸入字符串:\n");

gets(str);p=str;while(*p!='\0')p++;n=p-str;

printf("字符串:%s的長(zhǎng)度=%d\n",str,n);}2.字符串指針作函數(shù)參數(shù)當(dāng)數(shù)組名作為函數(shù)參數(shù)時(shí),在函數(shù)調(diào)用時(shí),實(shí)際傳遞給函數(shù)的是該數(shù)組的起始地址,即指針值。這樣,在函數(shù)形參說(shuō)明中,就可以將數(shù)組形參說(shuō)明為指針,并可以在函數(shù)體中通過(guò)指針存取或改變數(shù)組中的元素。例7_5編寫(xiě)一個(gè)合并兩個(gè)字符串的函數(shù)下面給出三種方法。(1)將形參說(shuō)明為數(shù)組,利用下標(biāo)引用數(shù)組元素voidmystrcat(charstr1[],charstr2[]){

inti=0,j=0;while(str1[i]!='\0')i++;/*找到字符串str1的結(jié)束符*/while(str2[j]!='\0'){str1[i]=str2[j];i++;j++;}/*把字符串str2的內(nèi)容復(fù)制到字符串str1中*/str1[i]='\0';/*添加字符串str1的結(jié)束符*/}(2)將形參說(shuō)明為指針,利用指針引用數(shù)組元素voidmystrcat(char*str1,char*str2){while(*str1!='\0')str1++;while(*str2!='\0'){*str1=*str2;str1++;str2++;}*str1='\0';}(3)將形參說(shuō)明為指針,利用下標(biāo)引用指針變量所指數(shù)組元素voidmystrcat(char*str1,char*str2){

inti=0,j=0;while(str1[i]!='\0')i++;/*找到字符串str1的結(jié)束符*/while(str2[j]!='\0'){str1[i]=str2[j];i++;j++;}/*把字符串str2的內(nèi)容復(fù)制到字符串str1中*/str1[i]='\0';/*添加字符串str1的結(jié)束符*/}主函數(shù)#include"stdio.h"main(){charsource[80]="StringOne",object[20]="stringtwo";

mystrcat(source,object);printf("str1+str2=%s",s

ource);}程序運(yùn)行結(jié)果是:str1+str2=StringOnestringtwo7.4.6指針數(shù)組1.指針數(shù)組的定義與應(yīng)用如果一個(gè)數(shù)組的每個(gè)元素都是指針類(lèi)型的數(shù)據(jù),則這種數(shù)組稱(chēng)為指針數(shù)組。指針數(shù)組定義的一般形式為:類(lèi)型標(biāo)識(shí)符*數(shù)組名[常量表達(dá)式];

例如:

char*p[10];表示p是一個(gè)指針數(shù)組,包括10個(gè)元素,每個(gè)元素都是字符型指針。在定義指針數(shù)組的同時(shí)也可以為其初始化。如:

char*name[]={“zhang

shan”,“Lishi”,“Wangwu”};

由初始表中的初值個(gè)數(shù)可以看出,name[]指針數(shù)組中共有3個(gè)元素,每個(gè)元素都是一個(gè)指針,如圖7-10所示。ZhangshanLishiWnagsu圖7-10指針數(shù)組name[0]name[1]name[2]其中name[0]指向字符串“Zhanshan”,name[1]指向字符串“Lishi”,name[2]指向字符串“Wangwu”。因此,語(yǔ)句:

printf(“%s,%s,%s\n”,name[0],name[1],name[2]);

將顯示字符串:Zhanshan,Lishi,Wangwu

在程序設(shè)計(jì)中,經(jīng)常使用指針數(shù)組顯示菜單信息。下面的例子就是這方面的實(shí)際應(yīng)用。[例7-5]利用指針數(shù)組顯示菜單信息FileEditSearchOption/*7_5.c*/#include"stdio.h"main(){char*menu[]={"File","Edit","Search","Option"};

inti;

for(i=0;i<4;i++)printf("%s",menu[i]);}如果利用C語(yǔ)言庫(kù)函數(shù)中的光標(biāo)定位函數(shù),則上述菜單信息可以顯示在屏幕的任意位置上,有關(guān)這方面的內(nèi)容,請(qǐng)查閱圖形和用戶(hù)界面方面的技術(shù)。2.指針數(shù)組作為mian()函數(shù)的形參指針數(shù)組的一個(gè)重要應(yīng)用就是作為main()函數(shù)的形參,在前面的程序中,main()函數(shù)是無(wú)參函數(shù)。實(shí)際上,main()可帶兩個(gè)參數(shù),其一般形式為:

main(int

argc,char*argv[])/*這里是形參罷了,名字是可以隨便取的,比如int

x,char*y[]*/第一個(gè)參數(shù)是整型,第二個(gè)參數(shù)是作為指向字符的指針數(shù)組來(lái)處理的,那么這兩個(gè)參數(shù)如何得到具體的值呢?我們知道,在DOS命令提示符下,可以鍵入一個(gè)可執(zhí)行文件名,還可以帶參數(shù)。如DOS命令:copyoldfile

newfile,將實(shí)現(xiàn)oldfile復(fù)制成newfile的功能。假如copy是用C語(yǔ)言實(shí)現(xiàn)的,則相應(yīng)的argc表示是參數(shù)的個(gè)數(shù)(含可執(zhí)行程序名),故argc的值為3,而指針數(shù)組argv中的元素argv[0]、argv[1]、argv[2]分別指向三個(gè)字符串“copy”、“oldfile”和“newfile”,如圖11-11所示。copy\0wldfile\0newfile\0圖7-11指針數(shù)組的應(yīng)用:命令行參數(shù)argv[0]argv[1]argv[2]argv/*echo.c*/#include<stdio.h>voidmain(int

argc,char*argv[]){

inti;

for(i=1;i<argc;i++)printf("%s",argv[i]);

printf("\n");}例如,下面程序echo.c將其所帶的參數(shù)顯示出來(lái),如在DOS狀態(tài)下鍵入echooldfile

newfile,將回顯oldfile

newfile。注意,若參數(shù)(含執(zhí)行文件名)共有n個(gè),則最后一個(gè)參數(shù)由指針argv[]指向。#include<stdio.h>voidUser(void){···/*提示用戶(hù)正確的操作信息語(yǔ)句*/}例7-6編寫(xiě)帶有幫助說(shuō)明的程序,也就是要求當(dāng)輸入執(zhí)行文件名,后跟“/?”時(shí),將提示命令行的操作方法。voidmain(int

argc,char*argv[]){

inti;

if(argc==2)if(strcmp(argv[1],"/?")==0){user();return;}···/*其他代碼*/}7.4.7指針與二維數(shù)組前面講過(guò),數(shù)組的指針是數(shù)組在內(nèi)存中的起始地址,數(shù)組元素的指針是數(shù)組元素在內(nèi)存中的起始地址;指向的數(shù)組的指針變量是指向該數(shù)組的指針變量,指向某數(shù)組元素的指針變量就是指向該數(shù)組元素的指針變量。對(duì)于多維數(shù)組雖然也是如此,但情況要復(fù)雜一些。實(shí)際上在程序中很少有用到超過(guò)二維的數(shù)組,因此在這里只討論二維數(shù)組與指針的關(guān)系。1.二維數(shù)組和數(shù)組元素的地址

C語(yǔ)言的二維數(shù)組由若干個(gè)一維數(shù)組構(gòu)成,即二維數(shù)組的每一個(gè)元素是一個(gè)一維數(shù)組。例如定義以下二維數(shù)組:

inta[3][4]={{1,3,5,7,},{9,11,13,15},{17,19,21,23,}};

a是一個(gè)數(shù)組名。a數(shù)組包含3行,即3個(gè)元素:a[0]、a[1]、a[2]。而每一個(gè)元素又是一個(gè)一維數(shù)組,每個(gè)一維數(shù)組又包含4個(gè)元素(即4個(gè)列元素),例如,a[0]所代表的一維數(shù)組又包含4個(gè)元素:a[0][0]、a[0][1]、a[0][2]、a[0][3],見(jiàn)圖7-12??梢哉J(rèn)為二維數(shù)組是“數(shù)組的數(shù)組”,即二維數(shù)組a是由3個(gè)一維數(shù)組所組成的。a[0]a[1]a[2]1917311195132171523圖7-12二維數(shù)組是“數(shù)組的數(shù)組”從二維數(shù)組的角度來(lái)看,a代表二維數(shù)組首元素的地址,現(xiàn)在的首元素不是一個(gè)簡(jiǎn)單的整型元素,而是由4個(gè)整型元素所組成的一維數(shù)組,因此a代表的是首行(即第0行)的首地址。a+1代表第1行的首地址。如果二維數(shù)組的首行的首地址為2000,則在TurboC中,a+1為2008,因?yàn)榈?行有4個(gè)整型數(shù)據(jù),因此a+1的含義是a[1]的地址,即a+4×2=2008。a+2代表a[2]的首地址,它的值是2016,見(jiàn)圖7-13。a數(shù)組a(2000)a+1a+2(2008)(2016)a[0]a[1]a[2]圖7-13二維數(shù)組各行

a[0]、a[1]、a[2]既然是一維數(shù)組名,而C語(yǔ)言又規(guī)定了數(shù)組名代表數(shù)組首元素地址,因此a[0]代表一維數(shù)組a[0]中第0列元素的地址,即&a[0][0]。a[1]的值是&a[1][0],a[2]的值是&a[2][0]。請(qǐng)考慮0行1列元素的地址怎么表示?a[0]為一維數(shù)組名,該一維數(shù)組中序號(hào)為1的元素的地址顯然應(yīng)該用a[0]+1來(lái)表示,見(jiàn)圖7-14。此時(shí)“a[0]+1”中的1代表1個(gè)列元素的字節(jié)數(shù),即2個(gè)字節(jié)。今a[0]的值是2000,a[0]+1的值是2002(而不是2008)。這是因?yàn)楝F(xiàn)在是在一維數(shù)組范圍內(nèi)討論問(wèn)題的,正如有一個(gè)一維數(shù)組x,x+1是其第1個(gè)元素x[1]的地址一樣。a[0]+0、a[0]+1、a[0]+2、a[0]+3分別是a[0][0]、a[0][1]、a[0][2]、a[0][3]元素的地址(即使&a[0][0]、&a[0][1]、&a[0][2]、&a[0][3])。aa+1a+2圖7-14二維數(shù)組各元素2000120023200452008920101120121320161720181920202120067201415202223a[0]a[0]+1a[0]+2a[0]+3前面已經(jīng)學(xué)過(guò),a[0]和*(a+0)等價(jià),a[1]和*(a+1)等價(jià),a[i]和*(a+i)等價(jià)。因此,a[0]+1和*(a+0)+1都是&a[0][1](即圖7-14中的2002)。a[1]+2和*(a+1)+2的值都是&a[1][2](即圖7-14中的2012)。請(qǐng)注意不要將*(a+1)+2錯(cuò)寫(xiě)成*(a+1+2),后者變成*(a+3)了,相當(dāng)于a[3]。進(jìn)一步分析,欲得到a[0][1]的值,用地址法怎么表示呢?既然a[0]+1和*(a+0)+1是a[0][1]的地址,那么,*(a[0]+1)就是a[0][1]的值。同理,*(*(a+0)+1)或*(*a+1)也是a[0][1]的值。*(a[i]+j)或*(*(a+i)+j)是a[i][j]的值。請(qǐng)務(wù)必記住*(a+i)和a[i]是等價(jià)的。2.指向二維數(shù)組及其元素的指針變量在了解上面的概念后,可以用指針變量指向二維數(shù)組或二維數(shù)組的元素。例7-7用指針變量輸出二維數(shù)組元素的值。/*7-7.c*/#include<stdio.h>voidmain(){

inta[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};

int*p=a;

inti;

for(i=0;i<=11;i++){if(i%4==0)printf(“\n”);printf("%4d",*(p+i));}

printf("\n");}程序運(yùn)行結(jié)果是:1357911131517192123這個(gè)p是指向數(shù)組元素的指針變量,數(shù)組元素是的類(lèi)型是int,因此p的定義形式是int*p=a;而且p+1是向后移動(dòng)2個(gè)字節(jié)。例7-8用指針變量輸出二維數(shù)組元素的值。#include<stdio.h>voidmain(){

inta[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};

inti=0,j=0;

int(*p)[4]=a;

for(i=0;i<3;i++)

for(j=0;j<4;j++){printf("%4d",*(*(p+i)+j));/*這里用p[i][j]也可以*/}

printf("\n");}程序運(yùn)行結(jié)果是:1357911131517192123這個(gè)p是指向數(shù)組元素的指針變量,數(shù)組元素是的類(lèi)型是int,因此p的定義形式是int*p=a;而且p+1是向后移動(dòng)2個(gè)字節(jié)。請(qǐng)注意這里變量p的定義“int(*p)[4]=a;”,由于p先和運(yùn)算符“*”結(jié)合,因此p是指向數(shù)組的指針變量。而如果是“int*p[4]”則由于p先與方括號(hào)結(jié)合,則p是個(gè)包含兩個(gè)某種元素的數(shù)組,然后再與*結(jié)合,表示p是由兩個(gè)指針構(gòu)成的數(shù)組,而且指針是指向int類(lèi)型的數(shù)據(jù)的,這樣的p就成了一個(gè)指針數(shù)組了。要區(qū)分這兩種形式,避免混淆。注:對(duì)于例7-8,為什么要把p定義成int(*p)[4]=a;而不能定義為int**p(見(jiàn)下節(jié)7.5.3的“3.思考”部分)在這里不作討論。7.4.8數(shù)組的存儲(chǔ)結(jié)構(gòu)之本質(zhì)前面我們對(duì)數(shù)組所作的討論是針對(duì)數(shù)組的邏輯結(jié)構(gòu),而事實(shí)上,如果我們能從數(shù)組的存儲(chǔ)結(jié)構(gòu)上進(jìn)行把握,那么多維數(shù)組可以徹底簡(jiǎn)化成簡(jiǎn)單的一維數(shù)組(而不是前面說(shuō)過(guò)的“把二維數(shù)組看作一維數(shù)組”)。邏輯結(jié)構(gòu)是針對(duì)邏輯、概念上的討論;存儲(chǔ)結(jié)構(gòu)是針對(duì)其在內(nèi)存中的儲(chǔ)存方式進(jìn)行的討論。例7-9對(duì)二維數(shù)組元素的一種簡(jiǎn)單訪問(wèn)形式。/*7-9.c*/voidmain(){

inta[3][3]={1,2,3,4,5,6,7,8,9};

int*p=&a[0][0];

printf("%d",*(p+8));}運(yùn)行結(jié)果為:

9可見(jiàn)該程序能夠通過(guò)*(p+8)訪問(wèn)二維數(shù)組的元素a[2][2]。很明顯,此時(shí)的訪問(wèn)更象對(duì)一維數(shù)組元素的引用方式。那么為什么可以這樣呢?事實(shí)上,無(wú)論是一維數(shù)組、二維數(shù)組還是多維數(shù)組,其數(shù)組元素事實(shí)上在內(nèi)存中都是線性、連續(xù)地存放的,所謂的“維度”不過(guò)是邏輯上的概念,不會(huì)應(yīng)為存在著“維度”而改變這種存儲(chǔ)結(jié)構(gòu)上的特征。因此,指針變量p指向了數(shù)組元素a[0][0],則只需通過(guò)對(duì)p進(jìn)行簡(jiǎn)單的移動(dòng)即可指向數(shù)組元素a[2][2],進(jìn)而達(dá)到訪問(wèn)該數(shù)組元素的效果。在這個(gè)過(guò)程中,任何維度的數(shù)組沒(méi)有任何本質(zhì)上的區(qū)別。圖7-15二維數(shù)組的存儲(chǔ)結(jié)構(gòu)和邏輯結(jié)構(gòu)123456789a[0][0]a[0][1]a[0][2]a[1][0]a[1][1]a[1][2]a[2][0]a[2][1]a[2][2]第一行a[0]第二行a[1]第三行a[2]存儲(chǔ)結(jié)構(gòu)邏輯結(jié)構(gòu)例7-10對(duì)二維數(shù)組元素的簡(jiǎn)單訪問(wèn)。/*7-10.c*/voidmain(){

inta[3][3]={1,2,3,4,5,6,7,8,9};

intj;

int*p=&a[1][1];

for(j=0;j<4;j+=2)

printf("%d",p[j]);}運(yùn)行結(jié)果為:

57思考:對(duì)于m行n列的二維數(shù)組a[m][n],如果知道了其某元素a[i][j]的地址,那么該數(shù)組任意元素a[x][y]的地址能用一個(gè)什么樣的通用表達(dá)式來(lái)代表呢?(注意:m和n是整常量,且0≤i≤m-1,0≤x≤m-1,0≤j≤n-1,0≤y≤n-1)請(qǐng)大家自己思考!總結(jié):在這里我們把握住了數(shù)組的存儲(chǔ)結(jié)構(gòu)而避開(kāi)其邏輯結(jié)構(gòu),這使得我們可以用一種簡(jiǎn)單的方式方便地訪問(wèn)多維數(shù)組的元素。事實(shí)上,邏輯結(jié)構(gòu)和存儲(chǔ)結(jié)構(gòu)統(tǒng)稱(chēng)為數(shù)據(jù)結(jié)構(gòu)?!稊?shù)據(jù)結(jié)構(gòu)》作為計(jì)算機(jī)專(zhuān)業(yè)的核心課程我們以后將會(huì)學(xué)到,在這里只是簡(jiǎn)單地提出,使得大家能有個(gè)提前的認(rèn)識(shí)。通過(guò)本節(jié)的學(xué)習(xí)大家也應(yīng)該看到:針對(duì)同一個(gè)問(wèn)題,抓住其不同方面的特性,有時(shí)能取得一些意想不到的便利和效果。7.5指向指針的指針指向指針變量的指針,簡(jiǎn)稱(chēng)為指向指針的指針;指向指針變量的指針變量簡(jiǎn)稱(chēng)為指向指針的指針變量。7.5.1指向指針的指針指向指針變量的指針,簡(jiǎn)稱(chēng)為指向指針的指針。在本章開(kāi)頭已經(jīng)提到了“間接訪問(wèn)”變量的方式。利用指針變量訪問(wèn)另一個(gè)變量就是“間接訪問(wèn)”。如果在一個(gè)指針變量中存放一個(gè)目標(biāo)變量的地址,這就是“單級(jí)間址”,見(jiàn)圖7-16(a)。指向指針的指針用的是“二級(jí)間址”方法,見(jiàn)圖7-16(b)。從理論上說(shuō),間址方法可以延伸到更多的級(jí),見(jiàn)圖7-16(c)。但實(shí)際上在程序中很少有超過(guò)二級(jí)間址的。級(jí)數(shù)越多,越難理解,容易產(chǎn)生混亂,出錯(cuò)機(jī)會(huì)也多。圖7-16通過(guò)指針變量存取變量的值地址值指針變量變量(a)一級(jí)間址地址1地址2指針變量1指針變量2(b)二級(jí)間址值變量地址n值變量指針變量n…指針變量指針變量2地址1地址2(c)n級(jí)間址7.5.2定義指向指針變量的指針變量指向指針變量的指針變量又簡(jiǎn)稱(chēng)為指向指針的指針變量。指向指針的指針變量定義的形式為:類(lèi)型名**指針變量明;此處,指針變量名是“指向指針的指針變量”的變量名,類(lèi)型名是該指針變量經(jīng)過(guò)二級(jí)間址后所存取變量的數(shù)據(jù)類(lèi)型。由于運(yùn)算符“*”的結(jié)合性是“從右到左”,因此“**指針變量名”等價(jià)于“*(*指針變量名)”,表示該指針變量的值存放的是另一個(gè)指針變量的地址,要經(jīng)過(guò)兩次間接存取后才能存取到變量的值。例如語(yǔ)句:

char**p;

定義p為指向指針的指針變量,它要經(jīng)過(guò)兩次間接存取后才能存取到變量的值,該變量的數(shù)據(jù)類(lèi)型為double。7.5.3指向指針的指針變量的應(yīng)用

1.指向一個(gè)指針變量,間接存取變量的值可以把一個(gè)指針變量的地址賦給指向指針的指針變量,然后通過(guò)二級(jí)間址方法存取變量的值。例7-11通過(guò)二級(jí)間址方法存取變量的值。/*7-11.c*/main(){doubled=123.456,*p,**pp;pp=&p;p=&d;

printf("d=%8.3f,",**pp);**pp+=543.21;

printf("d=%8.3f\n",d);}運(yùn)行結(jié)果為d=123.456,d=666.666上述指針變量pp指向指針變量p,而指針變量p又指向雙精度實(shí)型變量d,如圖7-17所示,圖中假設(shè)指針變量p的地址是1500,變量d的地址是3500。此時(shí)*pp表示指針變量p的值(即變量d的地址),因此表達(dá)式**pp與變量d等價(jià)。1500指向指針的指針變量pp3500指針變量p123.45變量d1500(地址)3500(地址)圖7-17指向指針指針指向指針變量2.指向指針數(shù)組,存取指針數(shù)組元素所指內(nèi)容可以把一個(gè)指針數(shù)組的首地址賦給指向指針的指針變量,例如:例7-12有三個(gè)等級(jí)分,由鍵盤(pán)輸入1,屏幕顯示“pass”,輸入2顯示“good”,輸入3顯示“excellent”。/*7_12.c*/main(){

intgrade;char*ps[]={"pass","good","excellent"},**pp;pp=ps;

printf("請(qǐng)輸入等級(jí)分(1~3):");

scanf("%d",&grade);

printf("%s\n",*(pp+grade-1));}運(yùn)行結(jié)果:請(qǐng)輸入等級(jí)(1~3):2↙good上述程序中pp指向數(shù)組

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論