




版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
指針—對(duì)存儲(chǔ)信息的引用機(jī)制8.1指針的概念8.2通過(guò)指針引用變量的值8.3通過(guò)指針引用一維數(shù)組元素8.4通過(guò)指針引用二維數(shù)組8.5通過(guò)指針引用字符串8.6通過(guò)指針調(diào)用函數(shù)8.7多重指針與指針數(shù)組8.8用于動(dòng)態(tài)內(nèi)存分配的指針型函數(shù)實(shí)操訓(xùn)練課外練習(xí)
8.1指?針?的?概?念
指針是什么?為什么通過(guò)指針可以訪問(wèn)存儲(chǔ)器中的信息?指針的一般意義大家都不會(huì)陌生,那就是建立一種指向,如儀器儀表的指針,路標(biāo)指針等。C語(yǔ)言指針也是建立一種指向,只是指向的是存儲(chǔ)器單元,按其所指訪問(wèn)存儲(chǔ)器單元中的信息。要理解C語(yǔ)言中指針的內(nèi)涵,需了解對(duì)存儲(chǔ)器的訪問(wèn)機(jī)制。
計(jì)算機(jī)對(duì)存儲(chǔ)器是按地址進(jìn)行訪問(wèn)的。存儲(chǔ)器包含大量的信息單元,為能方便地訪問(wèn)需要的信息單元,每一個(gè)單元都有一個(gè)編號(hào),這個(gè)編號(hào)稱(chēng)為地址。要訪問(wèn)某一個(gè)單元,只要給出該單元的地址,就能準(zhǔn)確引用該單元信息。打個(gè)通俗的比方,一個(gè)樓房包含許多房間,為能方便地尋找某個(gè)房間,給每一個(gè)房間編一個(gè)號(hào)碼,知道了房號(hào)就能準(zhǔn)確地找到這個(gè)房間。由此可知,地址就是對(duì)存儲(chǔ)器單元的指向,即給出地址就能訪問(wèn)存儲(chǔ)器單元,訪問(wèn)存儲(chǔ)器單元必須有地址。C語(yǔ)言中的指針就是存儲(chǔ)器單元的地址,也可以反過(guò)來(lái)說(shuō),地址就是存儲(chǔ)器單元的指針。簡(jiǎn)言之,指針就是地址,地址就是指針。因此,指針是對(duì)存儲(chǔ)器單元信息的一種引用機(jī)制。
地址屬于一個(gè)與存儲(chǔ)器硬件特性相關(guān)的概念。在高級(jí)語(yǔ)言中,編程人員不涉及計(jì)算機(jī)的硬件特性,對(duì)硬件資源的分配與處理由編譯系統(tǒng)來(lái)完成。在前面學(xué)習(xí)變量、數(shù)組、函數(shù)時(shí),僅是按語(yǔ)言規(guī)定進(jìn)行定義,按所定義的符號(hào)名稱(chēng)進(jìn)行引用,未涉及按地址引用的問(wèn)題。事實(shí)上,程序在編譯時(shí),系統(tǒng)給定義的數(shù)據(jù)對(duì)象或函數(shù)都要分配相應(yīng)的存儲(chǔ)單元,符號(hào)名稱(chēng)也具有相應(yīng)的地址值,符號(hào)名實(shí)際上標(biāo)識(shí)了一個(gè)存儲(chǔ)單元的地址。
比如,在一個(gè)程序中定義了3個(gè)整型變量i、j、k,程序編譯時(shí),系統(tǒng)就會(huì)給3個(gè)變量分別分配4個(gè)字節(jié)的存儲(chǔ)單元,單元中存儲(chǔ)變量的值,i、j、k也分別具有相應(yīng)的地址值。假定3個(gè)變量的值分別為2、4、8,編譯時(shí)給i、j、k分配的地址值分別是1000、1004、1008,其存儲(chǔ)情況如圖8.1所示。
圖8.1數(shù)據(jù)引用示意圖
在程序中,用變量名引用變量的值,在計(jì)算機(jī)內(nèi)部執(zhí)行時(shí),是按變量名所具有地址值的單元引用其存儲(chǔ)內(nèi)容。例如:
printf(“%d”,i);
輸出結(jié)果是1000地址單元的內(nèi)容“2”。
又如:
printf(“%X”,&i);
則輸出變量i的地址值“1000”。
對(duì)存儲(chǔ)器單元的訪問(wèn)有兩種方式:一種是直接訪問(wèn),另一種是間接訪問(wèn)。
直接訪問(wèn)是直接引用地址所指向單元中的內(nèi)容。按變量名引用變量的值屬于直接訪問(wèn)。
間接訪問(wèn)是將欲訪問(wèn)變量的地址存放在另一個(gè)變量中,先通過(guò)該變量取得欲訪問(wèn)變量的地址,再按這個(gè)地址引用變量的值。打個(gè)通俗的比方,從一個(gè)抽屜中存取東西,人們也可采取兩種方式:一種是將一個(gè)抽屜A的鑰匙帶在身上,需要時(shí)直接用A抽屜鑰匙,打開(kāi)A抽屜,存取所需東西;另一種是將A抽屜鑰匙放在另一抽屜B中,鎖起來(lái),需要打開(kāi)A抽屜時(shí),先要用B鑰匙打開(kāi)B抽屜,取出A鑰匙后,再打開(kāi)A抽屜,才能存取其中的東西。這就是一個(gè)間接訪問(wèn)的過(guò)程。
例如,我們將i變量所標(biāo)識(shí)的存儲(chǔ)單元的地址(1000)存放在i_pointer變量中,現(xiàn)要引用i變量的值,先要訪問(wèn)i_pointer變量,從中取得i變量的地址,然后才能按此地址所指向的i變量取得所需要的值。訪問(wèn)過(guò)程如圖8.1所示。
為了實(shí)現(xiàn)一個(gè)變量的間接訪問(wèn),需引入另外一種變量,稱(chēng)之為指針變量。如果一個(gè)變量專(zhuān)門(mén)用來(lái)存放另一個(gè)變量的地址(即指針),則稱(chēng)這個(gè)變量為指針變量。也就是說(shuō),指針變量的值是地址(即指針)。
從存儲(chǔ)器的訪問(wèn)機(jī)制,我們引入了“指針”和“指針變量”,弄清楚這兩個(gè)概念的內(nèi)涵,對(duì)學(xué)習(xí)本章后續(xù)內(nèi)容是至關(guān)重要的。
8.2通過(guò)指針引用變量的值
怎樣通過(guò)指針引用變量的值?從上節(jié)已知,通過(guò)指針可以引用變量的值,通過(guò)指針變量可以實(shí)現(xiàn)變量值的間接引用。使用指針和指針變量給變量值的引用帶來(lái)了許多靈活多變的方法和技巧,請(qǐng)讀者在本節(jié)內(nèi)容的學(xué)習(xí)中認(rèn)真體會(huì)。
8.2.1指針變量的定義與初始化
在程序中怎樣使用指針變量?
指針變量是有別于普通變量的,也必須先定義后使用。
指針變量定義的一般形式為
基類(lèi)型符*指針變量名;
其中,基類(lèi)型符是基本數(shù)據(jù)類(lèi)型關(guān)鍵字,如int、float、char、double等,用來(lái)指定所定義指針變量可以指向的變量類(lèi)型,簡(jiǎn)單地說(shuō),就是指針變量所指的數(shù)據(jù)對(duì)象的類(lèi)型。
“*”規(guī)定了所定義的變量是指針型變量。反過(guò)來(lái)說(shuō),沒(méi)有此星號(hào),那就成為普通變量的定義了。
指針變量名是所定義的指針變量的標(biāo)識(shí)符,其命名規(guī)則同普通變量定義中的變量名
一樣。
注:*和指針變量名之間可以有空格,也可以沒(méi)有空格,兩者均可。
下面是指針變量定義的幾個(gè)例子:
int*pi1;
語(yǔ)句定義了1個(gè)指針變量pi1,它只可以指向整型變量。
char*pc1,*pc2;
語(yǔ)句定義了2個(gè)指針變量pc1和pc2,它們只可以指向字符型變量。
float*pf1,*pf2,*pf3;
語(yǔ)句定義了3個(gè)指針變量pf1、pf2和pf3,它們只可以指向?qū)嵭妥兞俊?/p>
定義了指針變量,僅聲明了所定義的變量是指針類(lèi)型和可指向的變量類(lèi)型,并沒(méi)有確定的指向,也就是說(shuō),沒(méi)有具體指向哪一個(gè)變量或存儲(chǔ)器單元。打個(gè)通俗的比方,我們加工出一些指針,在沒(méi)有安裝到某一個(gè)儀器儀表前,它們沒(méi)有具體的指向。要使所定義的指針變量指向某一個(gè)變量,必須將指針變量初始化,將所要指向變量的地址賦給指針變量。指針變量中只能存放地址(即指針)。若要給指針變量賦數(shù)值則毫無(wú)意義。
指針變量初始化可有兩種方式:一是先定義指針變量,然后進(jìn)行初始化;二是定義指針變量的同時(shí)初始化。
這是一個(gè)先定義指針變量再初始化的例子。下面則是一個(gè)定義指針變量的同時(shí)初始化的例子:
注意:定義指針變量可指向變量的類(lèi)型與初始化賦地址的變量類(lèi)型必須保持一致,否則會(huì)出現(xiàn)錯(cuò)誤。如下面的初始化是錯(cuò)誤的。
因定義的兩個(gè)指針變量是指向整型數(shù)據(jù)的,而變量a、b被定義為實(shí)型,所以出現(xiàn)了類(lèi)型不一致的錯(cuò)誤。
8.2.2指針變量的引用
通過(guò)指針變量怎樣間接引用變量的值?
在指針變量的引用中要使用兩個(gè)運(yùn)算符“&”和“*”。“&”是取地址運(yùn)算符,例如&a是取變量a的地址;“*”是間接引用運(yùn)算符,其后跟指針變量,是取該指針變量所指向的數(shù)據(jù),例如?*p表示指針變量p所指向的數(shù)據(jù)。
指針變量有兩種引用:一是通過(guò)指針變量引用所指向變量的值,也就是說(shuō),通過(guò)指針變量實(shí)現(xiàn)變量值的間接引用;二是指針變量是變量,其值也可以引用,只不過(guò)引用的是地址(指針)值。下面通過(guò)兩個(gè)例子來(lái)說(shuō)明兩種引用和指針變量的應(yīng)用。
例8.1分析下面程序的運(yùn)行結(jié)果:
分析:程序中第1個(gè)printf函數(shù)調(diào)用中,輸出變量a、b的值,屬于變量值的直接引用,對(duì)應(yīng)執(zhí)行結(jié)果的第1行;第2個(gè)printf函數(shù)調(diào)用中,輸出指針變量pointer_1、pointer_2所指向變量的值,pointer_1指向變量a,pointer_2指向變量b,屬于變量值的間接引用,對(duì)應(yīng)執(zhí)行結(jié)果的第2行;第3個(gè)輸出函數(shù)調(diào)用中,以十六進(jìn)制格式輸出指針變量pointer_1、pointer_2的值,即變量a、b的地址,也屬于變量值的直接引用,其地址值是系統(tǒng)分配的。
例8.2利用指針?lè)椒▽?shí)現(xiàn):輸入兩個(gè)整數(shù),按先大后小的順序輸出。
編程思路:輸入兩個(gè)整數(shù),分別賦給兩個(gè)變量,比較兩個(gè)變量的值,利用指針變量可以不交換兩個(gè)變量的值,而只交換兩個(gè)指針變量的值(即改變指向)。
分析:輸入值2、8,即a<b,指針變量p1和p2的值進(jìn)行交換。交換前的情況如圖8.2(a)所示,交換后的情況如圖8.2(b)所示。
從圖可以看出,變量a、b的值并未交換,交換了指針變量p1、p2的值,實(shí)際上改變了指向,p1原指向a,交換值后指向b,p2原指向b,交換值后則指向a。這樣輸出*p1、*p2時(shí),就輸出變量b和變量a的值。
if語(yǔ)句中采用了兩個(gè)指針變量值的交換{p=p1;p1=p2;p2=p;},可改為{p1=&b;p2=&a;},效果是一樣的。圖8.2指針變化示意圖
8.2.3指針變量作函數(shù)參數(shù)
指針變量作函數(shù)參數(shù)傳遞什么值?函數(shù)之間能建立什么聯(lián)系?
在“函數(shù)”一章的學(xué)習(xí)中,已經(jīng)知道,變量作函數(shù)的參數(shù),把實(shí)參變量的值傳遞給形參變量。指針變量也可作函數(shù)的參數(shù),由于指針變量的值是地址(即指針),因此實(shí)參與形參之間傳遞的是指針,在被調(diào)函數(shù)中按指針的指向可得到變量的值。下面通過(guò)一個(gè)例子來(lái)說(shuō)明指針變量作函數(shù)的參數(shù)時(shí)指針傳遞的過(guò)程。
例8.3例8.2(輸入兩個(gè)整數(shù),按先大后小的順序輸出)中,用指針變量作參數(shù)的函數(shù)來(lái)實(shí)現(xiàn),并分析參數(shù)傳遞及其處理過(guò)程。
編程思路:用一個(gè)子函數(shù)實(shí)現(xiàn)交換兩個(gè)變量的值,主函數(shù)中判斷兩數(shù)大小,需要交換時(shí)調(diào)用子函數(shù),指針變量作函數(shù)參數(shù),傳遞指針。
分析:程序運(yùn)行時(shí)先執(zhí)行主函數(shù),輸入2、8,分別賦給變量a、b,兩個(gè)指針賦值語(yǔ)句使兩個(gè)指針變量分別指向變量a、b,如圖8.3(a)所示。執(zhí)行if語(yǔ)句,判斷a<b,調(diào)用swap函數(shù),兩個(gè)指針變量pointer_1、pointer_2的值(地址)傳遞給形參指針變量,使p1的值為&a,p2的值為&b,此時(shí)對(duì)應(yīng)的實(shí)參和形參指向同一個(gè)變量,如圖8.3(b)所示。執(zhí)行swap函數(shù),使p1、p2指向的兩個(gè)變量的值交換,如圖8.3(c)所示。函數(shù)調(diào)用結(jié)束后,形參p1、p2被釋放,即解除對(duì)變量a、b的指向,但在swap函數(shù)中的操作結(jié)果卻保留在變量a、b中,調(diào)用返回后輸出的a、b是交換后的值,如圖8.3(d)所示。圖8.3例8.3函數(shù)調(diào)用參數(shù)傳遞示意圖
通過(guò)本例應(yīng)該深刻理解,指針變量作函數(shù)參數(shù),實(shí)參傳遞地址(指針)給形參,使對(duì)應(yīng)的實(shí)參指針變量和形參指針變量指向同一個(gè)變量(存儲(chǔ)單元),在被調(diào)函數(shù)中通過(guò)形參指針變量對(duì)所指向的變量(存儲(chǔ)單元)的操作結(jié)果在主函數(shù)中引用,相當(dāng)于通過(guò)被操作的變量(存儲(chǔ)單元)傳送了值。這樣就給函數(shù)間增加了一種數(shù)據(jù)傳遞的渠道。需特別注意:通過(guò)實(shí)參和形參所指向變量值的傳遞是雙向的(在主調(diào)函數(shù)和被調(diào)函數(shù)中都可以引用變量的值),但實(shí)參指針變量向形參指針變量傳遞地址值仍是單向傳遞,也就是說(shuō),形參值不可能傳遞給實(shí)參,因?yàn)楹瘮?shù)調(diào)用結(jié)束后,形參就不復(fù)存在了。
變量作函數(shù)參數(shù),函數(shù)調(diào)用只能得到一個(gè)返回值(即函數(shù)值),使用指針變量作函數(shù)參數(shù)可以得到多個(gè)操作變量的值。如果想通過(guò)函數(shù)調(diào)用得到n個(gè)操作數(shù)據(jù),只需在函數(shù)中設(shè)置n個(gè)指針變量參數(shù),函數(shù)調(diào)用時(shí),將n個(gè)變量的指針傳遞給形參,在被調(diào)函數(shù)中通過(guò)形參指針變量對(duì)所指向變量進(jìn)行操作,在主調(diào)函數(shù)中就可得到操作結(jié)果。下面通過(guò)一個(gè)例子來(lái)說(shuō)明。
例8.4輸入3個(gè)整數(shù),按由大到小的順序輸出。
編程思路:對(duì)3個(gè)數(shù)排序,必須兩兩比較,根據(jù)大小關(guān)系進(jìn)行交換。這樣,需要3次比較,可能需要3次兩數(shù)的交換操作。為避免交換操作的重復(fù)書(shū)寫(xiě),可設(shè)計(jì)兩個(gè)函數(shù),一個(gè)函數(shù)實(shí)現(xiàn)兩數(shù)的交換功能,另一個(gè)函數(shù)進(jìn)行兩數(shù)比較,根據(jù)判定結(jié)果調(diào)用交換函數(shù)。
分析:在主函數(shù)中使指針變量pi1、pi2、pi3分別指向變量a、b、c,exchange(pi1,pi2,pi3)函數(shù)調(diào)用,將變量a、b、c的地址值分別傳遞給形參指針變量pj1、pj2、pj3。exchange(int*pj1,int*pj2,int*pj3)中,通過(guò)形參指針變量的間接引用來(lái)比較a與b、a與c、b與c,如果前者小于后者,調(diào)用swap函數(shù),將對(duì)應(yīng)變量的地址傳遞給形參指針變量pk1、pk2。在swap(int*pk1,int*pk2)中利用形參指針變量的間接引用來(lái)實(shí)現(xiàn)所指向變量值的交換。
8.3通過(guò)指針引用一維數(shù)組元素
怎樣通過(guò)指針引用一維數(shù)組元素?在“數(shù)組”一章中已經(jīng)知道,在程序中定義一個(gè)一維數(shù)組,程序編譯時(shí),系統(tǒng)給數(shù)組分配一個(gè)存儲(chǔ)區(qū),按順序存放數(shù)組元素,數(shù)組存儲(chǔ)區(qū)有一個(gè)首地址,每一個(gè)數(shù)組元素都有一個(gè)存儲(chǔ)單元地址。顯然,通過(guò)指針可以引用數(shù)組元素,通過(guò)指針變量可以實(shí)現(xiàn)數(shù)組元素的間接引用,而且可以采用靈活多樣的方式來(lái)引用數(shù)組元素。
8.3.1一維數(shù)組的存儲(chǔ)結(jié)構(gòu)與指針
如何建立數(shù)組元素與其指針的對(duì)應(yīng)關(guān)系?
一維“數(shù)組”的存儲(chǔ)是系統(tǒng)根據(jù)所定義的類(lèi)型,按元素順序分配一個(gè)連續(xù)的存儲(chǔ)空間。類(lèi)型規(guī)定了數(shù)組元素的存儲(chǔ)長(zhǎng)度,即一個(gè)元素所占的存儲(chǔ)字節(jié)數(shù)。char型數(shù)組一個(gè)元素占一個(gè)字節(jié),int型數(shù)組一個(gè)元素占2個(gè)或4個(gè)字節(jié),float型數(shù)組一個(gè)元素占4個(gè)字節(jié),double型數(shù)組一個(gè)元素占8個(gè)字節(jié)。例如,有語(yǔ)句“inta[5]={1,3,5,7,9};”,假定編譯系統(tǒng)分配的存儲(chǔ)區(qū)首地址是2000,則存儲(chǔ)結(jié)構(gòu)如圖8.4所示。圖8.4一維數(shù)組存儲(chǔ)結(jié)構(gòu)示意圖
在C語(yǔ)言中,可以不使用存儲(chǔ)地址來(lái)引用數(shù)組元素,而以數(shù)組名代表數(shù)組存儲(chǔ)首地址(指針),以數(shù)組元素的引用符號(hào)取地址運(yùn)算符代表元素地址(指針)。如a表示所定義數(shù)組首地址,&a[0]表示第0個(gè)元素地址,&a[1]表示第1個(gè)元素地址,……
知道數(shù)組及數(shù)組元素指針后,就可以定義指針變量來(lái)實(shí)現(xiàn)數(shù)組元素的間接引用。例如:
“p=a;”可以置換成“p=&a[0];”,這兩個(gè)語(yǔ)句是等效的,都使指針變量p指向所定義數(shù)組的首元素。
注意:數(shù)組名就表示數(shù)組首地址,一個(gè)數(shù)組元素相當(dāng)于一個(gè)變量,必須用“&”才能得到元素地址。
8.3.2一維數(shù)組指針調(diào)整與指針變量的運(yùn)算
如何調(diào)整指針及其對(duì)數(shù)組元素的指向?
對(duì)一維數(shù)組指針進(jìn)行一些調(diào)整運(yùn)算,可以產(chǎn)生靈活引用數(shù)組元素的方法,但指針的調(diào)整運(yùn)算也容易引發(fā)錯(cuò)誤,因此一定要弄清楚指針調(diào)整運(yùn)算的作用和意義。
1.使指針指向下一個(gè)元素:p+1,p++,++p
這3種運(yùn)算都可以使指針從當(dāng)前元素指向下一個(gè)元素。注意指針變量加1不是指針變量值加1的結(jié)果,而是下一個(gè)元素的地址值。如圖8.4所定義的數(shù)組,若“p=a;”或“p=&a[0];”,p的值是2000,p+1后使指針指向元素a[1],p+1的值是2004,而不是2001。若“p=&a[2];”,p的值是2008,p+1后使指針指向元素a[3],p+1的值是2012,而不是2009。由此可以理解為:指向數(shù)組指針變量的運(yùn)算,是以元素個(gè)數(shù)為單位的邏輯指針運(yùn)算,而不是純地址值的運(yùn)算。
p++和++p都可以使指針指向下一個(gè)元素,但引用元素時(shí)就有區(qū)別了。若“p=&a[2];”,則*(p++)是先引用元素a[2],再使p指針指向a[3];*(++p)先使指針指向a[3],再引用,引用的元素是a[3]。
2.使指針指向前一個(gè)元素:p-1,p--,--p
這3種運(yùn)算都可以使指針從當(dāng)前元素指向前一個(gè)元素。它們對(duì)指針的調(diào)整作用類(lèi)似于加1運(yùn)算。
p--、--p也是以元素個(gè)數(shù)為單位的邏輯指針運(yùn)算,而不是純地址值的運(yùn)算。
3.使指針指向第i個(gè)元素:a+i(a是所定義的數(shù)組名),p+i
這兩種運(yùn)算都可以使指針指向第i個(gè)元素,但有本質(zhì)的不同。a代表數(shù)組的首地址,也是首元素的地址,a屬于常量,a+i是數(shù)組中第i個(gè)元素的指針。p是指針變量,可以是數(shù)組中任一元素的指針,p+i是從當(dāng)前所指元素后移到第i個(gè)元素的指針。若“p=a;”或“p=&a[0];”,則a+i和p+i是一樣的,*(a+i)和*(p+i)都是引用數(shù)組a中的第i個(gè)元素。若“p=&a[2];”,則*(p+i)是引用a[2]后的第i個(gè)元素,即引用的元素是a[2+i]。
4.使指針指向前第i個(gè)元素:p-i
p-i是指針從p當(dāng)前所指的元素指向前第i個(gè)元素。例如“p=&a[4];”,則*(p-2)引用的是a[2]。注意,a-i是沒(méi)有意義的。
5.p2-p1運(yùn)算的意義
如果兩個(gè)指針變量p1、p2都指向同一數(shù)組,p2>p1,則p2-p1是兩個(gè)指針之間的元素個(gè)數(shù)。例如:
8.3.3通過(guò)指針引用數(shù)組元素
如何通過(guò)指針或指針變量引用數(shù)組元素?二者有何區(qū)別?
在“數(shù)組”一章中已經(jīng)介紹了“下標(biāo)法:a[i]”的形式引用數(shù)組元素。通過(guò)指針?lè)ǎ匆?(a+i)或*(p+i)形式引用數(shù)組元素,將使程序設(shè)計(jì)更方便和靈活。下面通過(guò)例子來(lái)說(shuō)明通過(guò)指針引用一維數(shù)組元素的方法。
例8.5一維數(shù)組的輸入與輸出。
下面用4種數(shù)組元素的引用方法編寫(xiě)程序,請(qǐng)認(rèn)真分析并體會(huì)數(shù)組元素的引用方法與編程技巧。
(1)下標(biāo)法。
(2)指針?lè)ā?/p>
運(yùn)行結(jié)果同下標(biāo)法。程序中使用了數(shù)組元素指針a+i和指針?lè)ㄒ脭?shù)組元素:*(a+i)。
(3)指針變量法(使用p++和*(p++))。思考:將*(p++)改為*(p+i),影響程序的正確性嗎??jī)蓚€(gè)“p=a;”語(yǔ)句的作用是什么?如果去掉第2個(gè)“p=a;”語(yǔ)句,程序的執(zhí)行結(jié)果會(huì)正確嗎?為什么?
(4)指針變量法(使用p++和*(--p))。
分析:顯然,程序運(yùn)行結(jié)果以逆序輸出。這是因?yàn)檠h(huán)輸入后,p指向最后一個(gè)元素之后,輸出中使用*(--p)先使p減1,指向前一個(gè)元素,從而產(chǎn)生了從后向前的輸出順序。
例8.6輸出一個(gè)數(shù)組中序號(hào)為0,3,6,9,…的元素。
8.3.4一維數(shù)組指針作函數(shù)參數(shù)
一維數(shù)組指針作函數(shù)參數(shù)傳遞什么值?函數(shù)間建立什么聯(lián)系?
用一維數(shù)組指針作函數(shù)參數(shù),實(shí)參向形參傳遞數(shù)組首地址,使形參指針和實(shí)參指針指向同一個(gè)數(shù)組存儲(chǔ)區(qū)。在調(diào)用函數(shù)期間,如果改變了形參數(shù)組的值,也就改變了實(shí)參數(shù)組的值,在主調(diào)函數(shù)中就可以引用改變后的值。如有下面定義的函數(shù)關(guān)系,則存儲(chǔ)區(qū)共享情況如圖8.5所示。圖8.5函數(shù)調(diào)用共享數(shù)組存儲(chǔ)區(qū)示意圖
數(shù)組指針可用數(shù)組名和指向數(shù)組的指針變量?jī)煞N形式傳遞。實(shí)參可以是數(shù)組名或指針變量,形參是接收實(shí)參傳遞的數(shù)組首元素地址的,應(yīng)該是指針變量。但因C語(yǔ)言程序編譯時(shí)是把形參數(shù)組名作為指針變量處理的,這樣形參也可以是數(shù)組名。數(shù)組指針作函數(shù)參數(shù),實(shí)參與形參可有表8.1列出的組合關(guān)系。
例8.7將一個(gè)數(shù)組中n個(gè)元素按相反的順序存放。
編程思路:定義n個(gè)元素的數(shù)組a,將a[0]與a[n-1]對(duì)換,再將a[1]與a[n-2]對(duì)換,……,直到將a[int(n-1)/2-1]與a[int(n-1)/2]對(duì)換。顯然,兩元素對(duì)調(diào)用循環(huán)結(jié)構(gòu)程序很容易實(shí)現(xiàn)。設(shè)“兩個(gè)位置指示”變量i和j,i指向首元素,其初值為0;j指向尾元素,其初值為n-1。使當(dāng)前所指示的兩個(gè)元素交換,然后調(diào)整指針,使i的值加1,j的值減1,如此循環(huán),直到i=int(n-1)/2,即直到兩指針指向中間兩相鄰的元素。其操作關(guān)系如圖8.6所示。圖8.6例8.7操作關(guān)系圖
下面用4種參數(shù)組合分別編寫(xiě)程序。
(1)用一個(gè)函數(shù)來(lái)實(shí)現(xiàn)交換,實(shí)參用數(shù)組名,形參用數(shù)組名。
分析:在定義函數(shù)inv時(shí),形參是數(shù)組名x,形參數(shù)組沒(méi)有定義大小,用n來(lái)接收數(shù)組元素個(gè)數(shù)。因?yàn)榫幾g系統(tǒng)把形參數(shù)組名作為一個(gè)指針變量處理,并不開(kāi)辟數(shù)組空間。在主函數(shù)中,函數(shù)調(diào)用語(yǔ)句“inv(a,10);”實(shí)參是數(shù)組名和數(shù)組元素個(gè)數(shù),把數(shù)組首元素地址傳遞給形參數(shù)組名x,數(shù)組元素的個(gè)數(shù)傳遞給形參n,使形參x指向?qū)崊?shù)組,在inv中操作數(shù)組,實(shí)際上就是實(shí)參數(shù)組。用函數(shù)名作為形參,可以用“下標(biāo)法”引用形參數(shù)組元素,有些人習(xí)慣這種直觀的引用方法。
(2)用一個(gè)函數(shù)來(lái)實(shí)現(xiàn)交換,實(shí)參用數(shù)組名,形參用指針變量。
運(yùn)行結(jié)果同(1)。在定義函數(shù)inv時(shí),指針變量作形參。引用形參數(shù)組時(shí),要用指針變量法。在inv函數(shù)中,定義了3個(gè)指針變量,i、j是指向當(dāng)前交換的前元素和后元素的指針變量,p是指向終止交換操作元素的指針變量。函數(shù)調(diào)用過(guò)程及參數(shù)傳遞同(1)。
(3)用一個(gè)函數(shù)來(lái)實(shí)現(xiàn)交換,實(shí)參用指針變量,形參用數(shù)組名。
(4)用一個(gè)函數(shù)來(lái)實(shí)現(xiàn)交換,實(shí)參用指針變量,形參用指針變量。
例8.8用指針?lè)▽?duì)n個(gè)整數(shù)按由大到小的順序排序。
編程思路:定義一個(gè)一維數(shù)組,存放n個(gè)整數(shù)。定義一個(gè)函數(shù),實(shí)現(xiàn)對(duì)n個(gè)整數(shù)排序。主函數(shù)中的函數(shù)調(diào)用傳遞被排序數(shù)組的首地址和數(shù)據(jù)個(gè)數(shù)。函數(shù)參數(shù)的組合關(guān)系可采用4種組合關(guān)系中的任一種。下面通過(guò)實(shí)參是指針變量、形參是數(shù)組名來(lái)實(shí)現(xiàn)。
分析:sort函數(shù)中用數(shù)組名作形參,用“下標(biāo)法”引用形參數(shù)組元素。如果形參改為指針變量,函數(shù)中也可以用與“下標(biāo)法”類(lèi)似的形式引用形參數(shù)組元素,可采用x+i和x+j作指針來(lái)引用數(shù)組元素。
可將函數(shù)修改如下:
8.4通過(guò)指針引用二維數(shù)組
怎樣通過(guò)指針引用二維數(shù)組元素?通過(guò)指針引用二維數(shù)組元素的原理和方法與一維數(shù)組元素引用基本相同,但因一維數(shù)組的存儲(chǔ)結(jié)構(gòu)與其邏輯結(jié)構(gòu)基本一致,而二維數(shù)組的存儲(chǔ)結(jié)構(gòu)與其邏輯結(jié)構(gòu)是不一致的,從而二維數(shù)組的指針比一維數(shù)組的指針要復(fù)雜一些。
8.4.1二維數(shù)組的存儲(chǔ)結(jié)構(gòu)與指針
如何建立二維數(shù)組元素與其指針的對(duì)應(yīng)關(guān)系?
二維數(shù)組的邏輯結(jié)構(gòu)是由行、列組成的平面結(jié)構(gòu)。因存儲(chǔ)器對(duì)數(shù)據(jù)的存儲(chǔ)結(jié)構(gòu)是線性的,所以要將二維平面結(jié)構(gòu)數(shù)據(jù)變換成一維線性結(jié)構(gòu)來(lái)存儲(chǔ)。二維數(shù)組是按行依次線性展開(kāi)存儲(chǔ)的。設(shè)有一個(gè)3行4列的二維整型數(shù)組,其定義為
inta[3][4]={{1,4,7,10},{2,5,8,11},{3,6,9,12}};
其邏輯結(jié)構(gòu)和存儲(chǔ)結(jié)構(gòu)的對(duì)應(yīng)關(guān)系如圖8.7所示。從圖中可以看出,按元素順序存第0行元素、第1行元素、第2行元素。
設(shè)定義一個(gè)n行m列的數(shù)組,則a[i][j]相對(duì)應(yīng)數(shù)組首元素的位置計(jì)算公式為
i?×?m?+?j
若定義了指針變量p,“p=&a[0][0];”,則元素a[i][j]的指針為
p?+?(i?×?m+j)
通過(guò)指針對(duì)元素a[i][j]的引用形式為
*(p?+?(i?×?m?+?j))
同一維數(shù)組指針一樣,上述表示的指針仍是以元素為單位的邏輯指針,不代表元素存儲(chǔ)的物理地址。如前面定義數(shù)組a的首地址為2000,a[2][1]的指針是&a[0][0]?+?(2?×?4?+?1),表示按存儲(chǔ)順序a數(shù)組的第9個(gè)元素,該元素的物理存儲(chǔ)地址應(yīng)為2000?+?9?×?4?=?2032。再一次強(qiáng)調(diào):C語(yǔ)言程序中,指向數(shù)組元素的指針是邏輯指針,并不是物理存儲(chǔ)地址。圖8.7二維數(shù)組存儲(chǔ)結(jié)構(gòu)示意圖
根據(jù)二維數(shù)組的線性存儲(chǔ)結(jié)構(gòu),二維數(shù)組可以看成由一維行數(shù)組組成的一維數(shù)組,如圖8.8所示。圖8.8二維數(shù)組一維行數(shù)組組成示意圖
可以把二維數(shù)組看成由兩層一維數(shù)組組成。第1層是每一行的4個(gè)元素組成一個(gè)一維數(shù)組,數(shù)組名分別為a[0]、a[1]、a[2]。第2層是以a[0]、a[1]、a[2]為元素的一維數(shù)組。第2層僅僅是一個(gè)邏輯上的一維數(shù)組。因?yàn)槎S數(shù)組的存儲(chǔ)結(jié)構(gòu)仍按元素存儲(chǔ)的線性結(jié)構(gòu),系統(tǒng)只給二維數(shù)組的元素分配存儲(chǔ)單元,a[0]、a[1]、a[2]雖是第2層一維數(shù)組的元素,但只表示每一行首元素的地址,并不占據(jù)存儲(chǔ)單元。這樣劃分的目的是把二維數(shù)組轉(zhuǎn)化為一維數(shù)組范圍內(nèi)的問(wèn)題,可以按一維數(shù)組元素的引用方法來(lái)分析二維數(shù)組元素的引用。
從一維數(shù)組元素的指針來(lái)看,a表示首元素地址,應(yīng)該有a=&a[0],但因a[0]只是邏輯層一維數(shù)組的元素,不占有存儲(chǔ)單元,談不上有地址,而a[0]表示第0行一維數(shù)組首元素的地址,即a[0]=&a[0][0]。又從a是二維數(shù)組的數(shù)組名,代表二維數(shù)組首元素地址,即有a=&a[0][0]。由此可推出a=a[0]=&a[0][0]。這正是問(wèn)題理解的關(guān)鍵。我們可以從邏輯引用的角度來(lái)分析,*(a+0)=a[0],*(a[0]+0)=a[0][0]。
為幫助理解,又引用本章前所打比方:從B抽屜取東西,將B鑰匙放在A抽屜,隨身帶A抽屜鑰匙,用A鑰匙打開(kāi)A抽屜,取出B鑰匙,再用B鑰匙打開(kāi)B抽屜。現(xiàn)在改為B抽屜鑰匙放在桌面上,只指示從桌面上拿到B鑰匙就可打開(kāi)B抽屜,即不需要用A鑰匙打開(kāi)A抽屜來(lái)得到B鑰匙,如圖8.9所示。也就是說(shuō),從第2層數(shù)組直接得到第1層數(shù)組元素的地址,再按地址引用第1層的數(shù)組元素。
圖8.9二維數(shù)組引用示意圖
對(duì)于任意元素,*(a+i)==a[i],*(a[i]+j)==a[i][j],即由*(a+i)引用第2層一維數(shù)組元素a[i],得到第1層行一維數(shù)組首元素地址,再由*(a[i]+j)引用第1層行一維數(shù)組元素a[i][j]。例如,*(a+1)==a[1],*(a[1]+2)==a[1][2]。
從以上分析可知,a、a+i、a[i]、*(a+i)、*(a+i)+j、a[i]+j都是地址(指針),而*(a[i]+j),*(*(a+i)+j)是對(duì)二維數(shù)組元素a[i][j]的引用。
由于把二維數(shù)組看成兩層一維數(shù)組的特殊性,定義指針變量也有別于直接指向二維數(shù)組元素的指針變量定義。其定義的一般形式為
基類(lèi)型符(*指針變量名)[m];
其中,m表示行一維數(shù)組元素個(gè)數(shù),即二維數(shù)組的列數(shù);基類(lèi)型符是二維數(shù)組元素的類(lèi)型。這樣定義,表示所定義的指針變量指向包含m個(gè)基類(lèi)型元素的一維數(shù)組,即指向邏輯層上的行一維數(shù)組。注意,指針變量?jī)蓚?cè)的括號(hào)不能缺省。
例如,int(*p)[4]定義了指向包含4個(gè)整型元素的一維數(shù)組的指針變量p。若“p=&a;”,則*(p+i)==a[i],*(*(p+i)+j)==a[i][j]。
在*(p+i)的指針調(diào)整中,是以第2層數(shù)組元素為單位的,而第2層數(shù)組中的一個(gè)元素表示二維數(shù)組的一行,所以(p+i)就使指針指向第i行。例如,(p+1)使指針指向第1行,(p+2)使指針指向第2行。
在*(p+i)+j的指針調(diào)整中,由于經(jīng)過(guò)*(p+i)運(yùn)算,得到a[i],即&a[i][0],它已經(jīng)轉(zhuǎn)化為指向列元素的指針了,因此加j是以元素為單位的,即加j就使指針指向第i行的第j個(gè)元素。例如,*(p+1)+1使指針指向第1行的第1列元素,*(p+1)+2使指針指向第1行的第2列元素。
8.4.2通過(guò)指針引用二維數(shù)組元素
如何通過(guò)指針或指針變量引用二維數(shù)組元素?
由前述已知,二維數(shù)組指針變量可有兩種定義,對(duì)數(shù)組元素的引用也有兩種方式。
1.通過(guò)指向元素的指針變量引用二維數(shù)組元素
設(shè)定義了n?×?m二維數(shù)組a,并定義了指向數(shù)組元素的指針變量p,則對(duì)數(shù)組元素的引用形式為
*(p?+?(i?×?(m?-?1)?+?j))
(i?×?(m?-?1)?+?j)是以元素為單位的相對(duì)位移量。若對(duì)p賦首元素指針,則引用a[i][j]元素;若p非首元素地址,則引用p當(dāng)前所指元素后的第i?×?(m?-?1)?+?j個(gè)元素。這種引用方式就是把二維數(shù)組的元素看作一個(gè)普通變量,通過(guò)定義指向變量的指針變量來(lái)間接引用數(shù)組元素。
2.通過(guò)指向一維數(shù)組的指針變量引用二維數(shù)組元素
設(shè)定義了二維數(shù)組a,并定義了指向一維數(shù)組的指針變量p,則對(duì)數(shù)組元素的引用形式為
*(*(p?+?i)?+?j)
若對(duì)p賦首元素指針,則引用a[i][j]元素;若p非首元素地址,則引用p當(dāng)前所指元素后的第i?×?m?+?j個(gè)元素。這種引用方式是把二維數(shù)組看作兩層一維數(shù)組來(lái)引用的。先定義指向行一維數(shù)組的指針變量p,*(p+i)取得第i行地址,*(p+i)+j就是第i行第j列元素的地址。
例8.9設(shè)有一個(gè)3?×?4的二維數(shù)組,要求用指向元素的指針變量以平面矩陣的形式輸出二維數(shù)組各元素的值。
編程思路:按二維數(shù)組的線性存儲(chǔ)結(jié)構(gòu),指向變量的指針變量可指向任一元素,可用單重循環(huán)結(jié)構(gòu)程序來(lái)實(shí)現(xiàn);也可結(jié)合邏輯結(jié)構(gòu)的行、列下標(biāo),對(duì)指針變量進(jìn)行運(yùn)算調(diào)整來(lái)引用任一元素,可用雙重循環(huán)程序結(jié)構(gòu)來(lái)實(shí)現(xiàn)。
(1)按存儲(chǔ)結(jié)構(gòu)引用元素。
(2)結(jié)合邏輯結(jié)構(gòu)下標(biāo),調(diào)整指針,用雙重循環(huán)實(shí)現(xiàn)程序。
分析:運(yùn)行結(jié)果同(1)。外循環(huán)控制行標(biāo),即控制行的順序;內(nèi)循環(huán)控制列標(biāo),即控制列的順序。p+i+j是指向a[i][j]的指針,依次指向二維數(shù)組的每一個(gè)元素。外循環(huán)每循環(huán)一次,p的值為&a[0][0]+i×3。第1次外循環(huán)時(shí),p的值為&a[0][0];第2次外循環(huán)時(shí),p的值為&a[0][0]+3;第3次外循環(huán)時(shí),p的值為&a[0][0]+6。分析清楚p值的變化,是理解使用指針變量引用數(shù)組元素的關(guān)鍵。
例8.10輸出二維數(shù)組任一行、任一列元素的值。
編程思路:由鍵盤(pán)輸入數(shù)組元素的行、列號(hào),根據(jù)行、列號(hào),使指針指向該元素輸出??墒褂弥赶蛐幸痪S數(shù)組的指針變量。
分析:定義了指向由4個(gè)整型元素組成的一維數(shù)組指針變量p,輸入2、3,*(p+i)+j=a[2]+3,即第2行的第3個(gè)元素。如果將第7行“p=a;”置換為“p=&a[0];”,或置換為“p=a+0;”,程序能正確編譯執(zhí)行。如果將第7行“p=a;”置換為“p=a[0];”,或置換為“p=&a[0][0];”,或置換為“p=*(a+0);”,程序編譯時(shí)都將出現(xiàn)如下出錯(cuò)信息。
前面已介紹,a、a+0、a[0]、&a[0][0]、*(a+0)都是數(shù)組首元素的地址。那么,上述情況為什么會(huì)不一樣呢?問(wèn)題的關(guān)鍵是:定義了“int(*p)[4];”,系統(tǒng)是按兩層一維數(shù)組進(jìn)行引用的,p是指向第2層由a[0]、a[1]、a[2]組成的一維數(shù)組的指針,只能指向這3個(gè)元素,而不能指向第1層每一行的數(shù)組元素。下面對(duì)幾種情況進(jìn)行分析。
(1)數(shù)組名a既表示二維數(shù)組首元素地址,又表示第2層數(shù)組首元素地址,“p=a;”使p指向第2層數(shù)組首元素。
(2)?a+0表示第2層一維數(shù)組首元素指針,“p=a+0;”同樣使p指向第2層數(shù)組首元素。
(3)?a[0]、a[1]、a[2]表示3個(gè)行一維數(shù)組的數(shù)組名,組成邏輯層的一維數(shù)組,雖然不占據(jù)存儲(chǔ)單元,不具有物理地址,但p=&a[0]屬于一種邏輯操作,其意義使p指向邏輯層的一維數(shù)組的首元素。
(4)?a[0]雖表示第0行首元素的地址,但它畢竟屬于第2層一維數(shù)組的元素,將數(shù)組元素直接賦指針變量是不符合規(guī)定的,所以出錯(cuò)。
(5)語(yǔ)句“int(*p)[4];”定義了p是指向行一維數(shù)組的指針,&a[0][0]表示二維數(shù)組首元素地址,“p=&a[0][0];”使p指向了二維數(shù)組元素,所以出現(xiàn)指向類(lèi)型錯(cuò)誤。
8.4.3二維數(shù)組指針作函數(shù)參數(shù)
二維數(shù)組指針作函數(shù)參數(shù)傳遞什么值?函數(shù)間建立什么聯(lián)系?
同一維數(shù)組指針作函數(shù)參數(shù)一樣,二維數(shù)組指針作函數(shù)參數(shù),參數(shù)間傳遞指針,使實(shí)參和形參指針共同指向一個(gè)數(shù)組,形參數(shù)組與實(shí)參數(shù)組共享一個(gè)二維數(shù)組存儲(chǔ)區(qū),在被調(diào)函數(shù)中對(duì)形參數(shù)組的操作結(jié)果,在主調(diào)函數(shù)中可以引用。用二維數(shù)組指針作函數(shù)參數(shù),可以方便靈活地處理二維表格數(shù)據(jù)。下面通過(guò)例子來(lái)說(shuō)明。
例8.11一個(gè)班有n個(gè)學(xué)生,開(kāi)設(shè)m門(mén)課程,計(jì)算總平均分,并輸出任一學(xué)生各門(mén)課成績(jī)。
編程思路:
(1)為了突出程序設(shè)計(jì)方法和程序結(jié)構(gòu),而不被眾多的數(shù)據(jù)干擾,先設(shè)有3個(gè)學(xué)生、4門(mén)課程。在此基礎(chǔ)上,只要添加數(shù)據(jù),設(shè)置相應(yīng)變量的初值,就能實(shí)現(xiàn)更多學(xué)生、更多課程的處理。
(2)班級(jí)成績(jī)表顯然屬于二維數(shù)據(jù)表格,按功能設(shè)計(jì)處理函數(shù),按求總平均分設(shè)計(jì)一個(gè)處理函數(shù),按輸出一個(gè)學(xué)生各門(mén)課成績(jī)?cè)O(shè)計(jì)一個(gè)處理函數(shù),數(shù)組指針作函數(shù)參數(shù),減少數(shù)據(jù)傳遞數(shù)量,提高程序效率。
分析:
(1)求平均分函數(shù)average定義中,第一個(gè)形參p定義為指向?qū)嵭蛿?shù)組元素的指針,第2個(gè)形參n是二維數(shù)組元素的個(gè)數(shù),即全班學(xué)生所有課程的成績(jī)數(shù)目。函數(shù)調(diào)用時(shí),第1個(gè)實(shí)參?*score是第2層邏輯一維數(shù)組首元素的引用,即score[0],也就是&score[0][0],即score[0][0]的地址,使形參p指向成績(jī)數(shù)組首元素。
(2)查找并輸出任一個(gè)學(xué)生成績(jī)函數(shù)search定義中,第1個(gè)參數(shù)定義p為指向第2層行一維數(shù)組的指針,第2個(gè)參數(shù)n表示學(xué)號(hào)。函數(shù)調(diào)用時(shí),第1實(shí)參是score,也代表第2層行一維數(shù)組首元素score[0]的指針,使形參p指向第0個(gè)學(xué)生的第0門(mén)課程成績(jī)。*(*(p+n)+i)表示第n個(gè)學(xué)生的第i門(mén)課程分?jǐn)?shù),即元素score[n][i]的值。
特別強(qiáng)調(diào):實(shí)參和形參如果是指針類(lèi)型,應(yīng)當(dāng)注意它們類(lèi)型的一致性。不能把int*型的指針(即元素的地址)傳給int(*)[]型(指向一維數(shù)組)的指針變量,反之亦然。例如,函數(shù)調(diào)用score(score,2)置換成score(*score,2)就產(chǎn)生錯(cuò)誤。因?yàn)?score==score[0]==&score[0][0]是二維數(shù)組首元素地址,而形參是int(*)[]型(指向一維數(shù)組)的指針變量。
例8.12在例8.11的基礎(chǔ)上,查找有一門(mén)以上課程不及格的學(xué)生,并輸出這些學(xué)生全部課程的成績(jī)。
分析:search函數(shù)定義中,第一個(gè)形參p定義為指向第2層一維數(shù)組的指針,第2個(gè)形參n是班級(jí)學(xué)生數(shù)。在函數(shù)中定義了一個(gè)標(biāo)志變量flag,在內(nèi)循環(huán)外賦初值0,在內(nèi)循環(huán)中若查出當(dāng)前查找的學(xué)生有不及格課程,則賦值1。結(jié)束內(nèi)循環(huán)后,檢查標(biāo)志,如果flag為1,則通過(guò)一個(gè)循環(huán)輸出該學(xué)生的號(hào)碼和各門(mén)課成績(jī)。函數(shù)調(diào)用時(shí),傳遞實(shí)參score,即score[0]的地址,使形參p指向score數(shù)組的第0行。
8.5通過(guò)指針引用字符串
怎樣通過(guò)指針引用字符數(shù)組中的字符?在前面的章節(jié)中已經(jīng)使用過(guò)字符串,但都是以字符串常量的形式出現(xiàn)的,即用一對(duì)雙撇號(hào)界定的字符串。在“字符數(shù)組”一節(jié)中,介紹了通過(guò)字符數(shù)組存儲(chǔ)及引用字符串的方法,字符串還可以通過(guò)指針來(lái)引用。
8.5.1字符串的存儲(chǔ)結(jié)構(gòu)與指針
如何建立字符串中字符與其指針的對(duì)應(yīng)關(guān)系?
在前面已經(jīng)介紹過(guò),字符串是按字符數(shù)組進(jìn)行存儲(chǔ)的,只是在最后一個(gè)字符后加存一個(gè)字符串結(jié)束符“\0”。例如,charstring[]="IloveChina!",其存儲(chǔ)結(jié)構(gòu)如圖8.10所示。根據(jù)數(shù)組指針的概念,字符串指針就是其存儲(chǔ)的首地址,字符數(shù)組名也表示字符串的首地址圖8.10字符串存儲(chǔ)結(jié)構(gòu)與指針
8.5.2通過(guò)指針引用字符串
如何通過(guò)指針或指針變量引用字符數(shù)組(字符串)字符?
同前面講過(guò)的數(shù)組引用一樣,通過(guò)指針引用字符串也有兩種方法:一種是通過(guò)所定義的字符數(shù)組名(指針)引用;另一種是通過(guò)所定義的字符串指針變量來(lái)引用。
1.通過(guò)字符數(shù)組名(指針)引用
在“字符數(shù)組”一節(jié)中已經(jīng)介紹,通過(guò)字符數(shù)組名可以引用字符串,使用“%s”格式描述符,可以整體輸入或輸出,只不過(guò)沒(méi)有引入指針的概念。字符數(shù)組名就表示字符串指針,通過(guò)指針可以對(duì)字符串及字符串中的字符進(jìn)行引用。下面通過(guò)一個(gè)例子說(shuō)明引用方法。
例8.13定義一個(gè)字符數(shù)組,存放字符串“IloveChina!”,輸出該字符串和第8個(gè)元素。
分析:在“printf("%s\n",string);”中使用%s格式描述符和數(shù)組名(指針)對(duì)字符串整體輸出,這里與數(shù)值型數(shù)組不同。在C語(yǔ)言中,“%s”格式描述符與指針配合,能實(shí)現(xiàn)字符串的整體輸出或輸入。先使指針指向串的首字符,輸出,指針自動(dòng)加1,指向下一個(gè)字符,依次輸出,直到字符串結(jié)束符“\0”。在“printf("No.8%c\n",*(string+7));”中的“*(string+7)”是用指針引用字符串中第7個(gè)元素string[7],因數(shù)組元素編號(hào)從0開(kāi)始,實(shí)際上就是串中第8個(gè)字符。
2.通過(guò)字符串指針變量引用
指向字符串的指針變量,也就是指向字符數(shù)組的指針變量。指向字符數(shù)組的指針變量也必須先定義后使用,而且指針變量只有賦初值后才能建立指向。
字符型指針變量定義的一般形式為
char*指針變量名;
其中,char屬于基類(lèi)型,規(guī)定了指針變量指向字符型數(shù)據(jù)(即指向字符串),其他部分同指針變量定義的意義一樣。
給字符型指針變量賦初值的方式也有兩種:一是定義的同時(shí)賦初值;二是先定義,再賦初值。
例如,下面兩種定義和賦初值的方式是等價(jià)的。
方法一:
char*p_string="IloveChina!";
方法二:
char*p_string;
p_string="IloveChina!";
注意:給字符型指針變量賦初值與給數(shù)值型指針變量賦初值是不一樣的。從形式上看,給字符型指針變量賦了一個(gè)字符串,實(shí)際上,系統(tǒng)是把字符串首地址賦給字符型指針變量。可以這樣想象,字符型指針變量是一個(gè)變量,只占一個(gè)存儲(chǔ)單元,它不可能存放一個(gè)任意長(zhǎng)度的字符串。所以,不要理解為把一個(gè)字符串賦給了一個(gè)字符型指針變量。
例8.14通過(guò)指針變量來(lái)實(shí)現(xiàn)例8.13的功能。
分析:與例8.13相比,只是定義了字符型指針變量,用指針變量來(lái)引用字符串和字符串中的字符,運(yùn)行結(jié)果也完全一樣。但仍要注意,用數(shù)組名作指針,其地址值是固定的,而用字符型指針變量,其值是可以變化的。在本例中體現(xiàn)得不是太明顯,但在比較復(fù)雜的問(wèn)題中就會(huì)表現(xiàn)出來(lái)。
例8.15將字符串a(chǎn)復(fù)制到字符串b中,再輸出字符串b。
編程思路:對(duì)字符串的輸入或輸出,可以使用%s格式描述符,進(jìn)行整體的輸入或輸出。但對(duì)字符串的其他操作,只能對(duì)字符串中的字符逐個(gè)進(jìn)行處理,即對(duì)字符數(shù)組中的元素逐個(gè)進(jìn)行處理。所以,對(duì)字符串的處理,常采用循環(huán)結(jié)構(gòu)。下面分別通過(guò)指針引用法和指針變量引用法來(lái)編寫(xiě)程序。
分析:
①數(shù)組b的長(zhǎng)度應(yīng)大于等于數(shù)組a,否則無(wú)法完整復(fù)制。
②復(fù)制循環(huán)中使用了指針引用法,把a(bǔ)數(shù)組中的字符依次逐個(gè)復(fù)制到b數(shù)組中,循環(huán)結(jié)束的判定條件是字符串結(jié)束符“\0”,不能把結(jié)束符復(fù)制到b數(shù)組中。所以循環(huán)結(jié)束后,向b數(shù)組后添加結(jié)束符“\0”。
③輸出循環(huán)中使用了數(shù)組下標(biāo)法,可以與指針引用法比較。
分析:該程序的運(yùn)行結(jié)果同(1)。循環(huán)復(fù)制中使用指針變量法實(shí)現(xiàn)逐個(gè)字符復(fù)制,指針變量既作指針,又作循環(huán)控制變量,使p1、p2同步移動(dòng)。這也是指針變量使用的一種技巧。兩字符串的輸出仍采用了數(shù)組名(指針?lè)?,體現(xiàn)了通過(guò)指針引用字符串的多種方法。
8.5.3字符指針作函數(shù)參數(shù)
字符指針作函數(shù)參數(shù)傳遞什么值?函數(shù)間建立什么聯(lián)系?
同數(shù)組指針作函數(shù)參數(shù)一樣,字符指針作函數(shù)參數(shù),參數(shù)間傳遞指針值,使實(shí)參和形參指針共同指向一個(gè)字符串存儲(chǔ)區(qū)。在被調(diào)函數(shù)中可以改變字符串的內(nèi)容,主調(diào)函數(shù)中可以引用改變后的字符串。實(shí)參和形參的形式同樣可有4種組合關(guān)系。
例8.16用函數(shù)調(diào)用實(shí)現(xiàn)字符串的復(fù)制。
編程思路:定義一個(gè)函數(shù)來(lái)實(shí)現(xiàn)字符串復(fù)制的功能,在主函數(shù)中提供源串,調(diào)用復(fù)制函數(shù)后,輸出源串和目標(biāo)串。函數(shù)參數(shù)可以是字符數(shù)組名(字符串指針)或字符指針變量。下面分別給出實(shí)參和形參4種組合關(guān)系的程序,以供分析比較,體會(huì)編程方法與技巧。
(1)實(shí)參、形參都用字符數(shù)組名(指針)。
分析:程序中函數(shù)實(shí)參和形參都是數(shù)組名,系統(tǒng)仍把形參數(shù)組名作指針變量處理。在函數(shù)調(diào)用時(shí),將a和b第1個(gè)字符的地址分別傳給形參from和to,使from指向字符串a(chǎn),to指向字符串b。函數(shù)調(diào)用后,字符串b產(chǎn)生變化。函數(shù)調(diào)用前后字符串?dāng)?shù)組的狀態(tài)如圖8.11(a)和(b)所示。從圖中可以看到,由于b數(shù)組原來(lái)長(zhǎng)度大于a數(shù)組,在a數(shù)組復(fù)制到b數(shù)組后,未能全部覆蓋b數(shù)組原有內(nèi)容。b數(shù)組最后3個(gè)元素仍保留原狀。在輸出b時(shí),由于按%s格式輸出,遇到“\0”即結(jié)束,因此第1個(gè)“\0”后的字符不輸出。如果采用%c格式,則后面的字符是可以輸出的。圖8.11函數(shù)調(diào)用前后的字符數(shù)組狀態(tài)
(2)實(shí)參用字符數(shù)組名,形參用字符指針變量。
分析:程序的運(yùn)行結(jié)果同(1)。形參是指針變量,調(diào)用時(shí)接收參數(shù)傳遞的源串和目標(biāo)串的首地址,利用指針變量加1調(diào)整運(yùn)算,能很方便地使用for循環(huán)來(lái)實(shí)現(xiàn)逐個(gè)字符的復(fù)制。
(3)實(shí)參用字符指針變量,形參用字符數(shù)組名。
分析:程序的運(yùn)行結(jié)果同(1)。參數(shù)傳遞的是指針變量,但被賦給字符數(shù)組名,調(diào)用時(shí),傳遞的是字符串首地址。形參是數(shù)組名,編譯系統(tǒng)按指針變量看待,可接收實(shí)參傳遞的字符串首地址,在字符復(fù)制循環(huán)中用“下標(biāo)法”實(shí)現(xiàn)逐個(gè)字符復(fù)制。
(4)實(shí)參、形參都用字符指針變量。
分析:程序的運(yùn)行結(jié)果同(1)。參數(shù)傳遞及函數(shù)調(diào)用過(guò)程請(qǐng)讀者自行分析。
字符型指針作函數(shù)參數(shù),引用字符串元素的方法有多種,給程序設(shè)計(jì)帶來(lái)許多技巧。下面通過(guò)對(duì)copy_string函數(shù)的改寫(xiě)來(lái)說(shuō)明程序設(shè)計(jì)的方法與技巧。
8.6通過(guò)指針調(diào)用函數(shù)
通過(guò)指針可以調(diào)用函數(shù)嗎?在程序中定義的每一個(gè)函數(shù),編譯時(shí)均生成代碼,系統(tǒng)為其分配一定的存儲(chǔ)空間。一個(gè)函數(shù)代碼存儲(chǔ)空間的起始地址就是函數(shù)調(diào)用的入口地址。在引入指針之前,用函數(shù)名調(diào)用函數(shù)。事實(shí)上是用函數(shù)名表示函數(shù)的入口地址。本質(zhì)上,函數(shù)調(diào)用會(huì)使程序指針轉(zhuǎn)到函數(shù)入口處執(zhí)行。通過(guò)指針或指針變量調(diào)用函數(shù),反映函數(shù)調(diào)用的實(shí)質(zhì),會(huì)產(chǎn)生函數(shù)調(diào)用的靈活方法,實(shí)現(xiàn)一些用函數(shù)名調(diào)用實(shí)現(xiàn)不了的功能。
8.6.1函數(shù)指針與指針變量的定義
何謂函數(shù)指針?在程序中如何使用函數(shù)指針或函數(shù)指針變量?
函數(shù)的指針就是函數(shù)代碼存儲(chǔ)的起始地址,也就是函數(shù)調(diào)用時(shí)的入口地址。可以通過(guò)指向函數(shù)入口的指針變量(或簡(jiǎn)稱(chēng)指向函數(shù)的指針變量)來(lái)調(diào)用函數(shù)。
指向函數(shù)的指針變量又是一種新類(lèi)型的指針變量,也必須先定義后使用。指向函數(shù)的指針變量定義的一般形式為
基類(lèi)型符(*變量名)(函數(shù)參數(shù)表列)
其中,基類(lèi)型符是指函數(shù)返回值的類(lèi)型。變量名前加*表示是指針變量,兩側(cè)再加括號(hào),連同后面的括號(hào)內(nèi)的函數(shù)參數(shù)表列一起,表示所定義的變量是指向返回值為基類(lèi)型的函數(shù)的指針變量。也就是說(shuō),變量名兩側(cè)的括號(hào)和其后的括號(hào)與參數(shù)列表都不能遺漏。
例如:
int(*p)(int,int);
定義了指向返回值為整型、有兩個(gè)整型參數(shù)的函數(shù)的指針變量p。
與一般指針變量定義和使用一樣,定義了一個(gè)指向函數(shù)的指針變量后,并沒(méi)有指向哪一個(gè)函數(shù),只有當(dāng)賦初值后,才能建立起明確的指向。如果把一個(gè)已定義的函數(shù)名賦給已定義的函數(shù)的指針變量,則指針變量與函數(shù)就建立了明確的指向。
8.6.2通過(guò)函數(shù)指針調(diào)用函數(shù)
如何通過(guò)函數(shù)指針或指針變量調(diào)用函數(shù)?
通過(guò)函數(shù)指針調(diào)用函數(shù)有兩種方式:一是通過(guò)函數(shù)指針(函數(shù)名);二是通過(guò)指向函數(shù)的指針變量。通過(guò)函數(shù)名調(diào)用已經(jīng)介紹過(guò),因函數(shù)名僅表示一個(gè)固定地址,故只能調(diào)用一個(gè)固定的函數(shù)。指向函數(shù)的指針變量的值可以變化,可使其指向不同的函數(shù),分別實(shí)現(xiàn)不同函數(shù)的調(diào)用。
例8.17用函數(shù)求兩個(gè)整數(shù)中的大數(shù)。
編程思路:定義一個(gè)函數(shù),通過(guò)比較求出兩個(gè)整數(shù)中的大數(shù)。在主函數(shù)中定義指向函數(shù)的指針變量,通過(guò)指針變量調(diào)用函數(shù),得到兩數(shù)中的大數(shù)。
分析:程序第5行定義了指向返回值為整型、有兩個(gè)整型參數(shù)函數(shù)的指針變量p。第7行將函數(shù)名表示的函數(shù)起始地址賦給指針變量p,使其指向函數(shù)max。第10行“c=(*p)(a,b);”是指向函數(shù)的指針變量對(duì)函數(shù)的調(diào)用,a、b是實(shí)參,把函數(shù)的返回值賦給變量c。函數(shù)調(diào)用過(guò)程及參數(shù)傳遞同函數(shù)名調(diào)用。如果將“c=(*p)(a,b);”置換為“c=max(a,b);”且不需定義指向函數(shù)的指針變量和賦初值語(yǔ)句,則程序的執(zhí)行結(jié)果完全一樣。這個(gè)例子說(shuō)明了通過(guò)指向函數(shù)的指針變量調(diào)用函數(shù)的方法,但看不出通過(guò)指向函數(shù)的指針變量調(diào)用函數(shù)的優(yōu)越性。在后面將會(huì)看到,利用指針變量值的可變性,可以實(shí)現(xiàn)一類(lèi)函數(shù)的調(diào)用。.
8.6.3用指向函數(shù)的指針作函數(shù)的參數(shù)
函數(shù)指針作函數(shù)參數(shù)傳遞什么值?調(diào)用函數(shù)與被調(diào)函數(shù)間建立什么關(guān)系?
用指向函數(shù)的指針作函數(shù)參數(shù),實(shí)參把函數(shù)的入口地址傳遞給形參,在被調(diào)函數(shù)中就可以通過(guò)形參來(lái)調(diào)用實(shí)參所指向的函數(shù)。通常,把一類(lèi)函數(shù)(多個(gè)功能操作的函數(shù))定義在一個(gè)函數(shù)中,由指向函數(shù)的指針變量作參數(shù),通過(guò)實(shí)參傳遞不同函數(shù)的入口地址,就可以實(shí)現(xiàn)一類(lèi)函數(shù)的調(diào)用,相當(dāng)于具有了C++類(lèi)的特點(diǎn)。這正是函數(shù)指針作函數(shù)參數(shù)的一個(gè)重要用途。下面通過(guò)一個(gè)簡(jiǎn)單的例子說(shuō)明這種方法的應(yīng)用。
例8.18實(shí)現(xiàn)兩個(gè)整數(shù)的加、減、乘、除運(yùn)算,用戶(hù)通過(guò)輸入1、2、3、4分別進(jìn)行加法、減法、乘法、除法運(yùn)算并輸出結(jié)果。
編程思路:分別設(shè)計(jì)實(shí)現(xiàn)兩整數(shù)加、減、乘、除的函數(shù),再設(shè)計(jì)一個(gè)參數(shù)為指針的函數(shù),在主函數(shù)中根據(jù)用戶(hù)的選擇,傳遞相應(yīng)運(yùn)算函數(shù)的指針,形參接收函數(shù)指針后,調(diào)用不同的運(yùn)算函數(shù)。
分析:在arith函數(shù)定義中,x、y是兩個(gè)接收運(yùn)算數(shù)的形參,int(*p)(int,int)是指向函數(shù)的指針變量,接收函數(shù)的指針。在函數(shù)體中,由指針調(diào)用相應(yīng)運(yùn)算函數(shù),相當(dāng)于構(gòu)建了一個(gè)四則運(yùn)算類(lèi)函數(shù)。
程序運(yùn)行時(shí),在主函數(shù)中接收用戶(hù)的運(yùn)算選擇,switch語(yǔ)句依據(jù)選擇調(diào)用arith函數(shù),把兩運(yùn)算數(shù)和相應(yīng)功能運(yùn)算函數(shù)指針傳遞給形參,由“result=(*p)(x,y);”語(yǔ)句調(diào)用相應(yīng)功能運(yùn)算函數(shù)。顯然,p是指向函數(shù)的指針變量,可以接收不同函數(shù)的指針(函數(shù)入口地址),通過(guò)它來(lái)實(shí)現(xiàn)多個(gè)函數(shù)的調(diào)用。參數(shù)傳遞及p的指向如圖8.12所示。圖8.12例8.17參數(shù)傳遞及指針變量的指向
從本例可以清楚地看到,通過(guò)用戶(hù)選擇可以實(shí)現(xiàn)調(diào)用add、sub、mul、div函數(shù),函數(shù)arith沒(méi)有改變,只是實(shí)參傳遞不同的函數(shù)名而已。在arith函數(shù)中輸出result,在不同的情況下,調(diào)用不同函數(shù)得到的result值是不同的。這就增加了函數(shù)使用的靈活性,相當(dāng)于具有C++類(lèi)的特點(diǎn)。
8.6.4返回指針值的函數(shù)
函數(shù)返回指針值,調(diào)用函數(shù)與被調(diào)函數(shù)間建立什么聯(lián)系?
函數(shù)的返回值可以是指針。指針作為函數(shù)的返回值,可以減少函數(shù)間傳遞的數(shù)據(jù)量,是提高程序效率的有效方法。特別是在主調(diào)函數(shù)和被調(diào)函數(shù)都對(duì)一個(gè)數(shù)組進(jìn)行操作的情況下,數(shù)組指針作函數(shù)的返回值,在主調(diào)函數(shù)中,通過(guò)返回指針就可以引用操作后的數(shù)組
元素。
返回指針值的函數(shù),要將返回值類(lèi)型定義為指針型。其定義的一般形式為
基類(lèi)型符*函數(shù)名(參數(shù)表列);
顯然,返回指針值函數(shù)的定義和返回基本類(lèi)型值函數(shù)的定義的區(qū)別僅在于基類(lèi)型符和函數(shù)名之間加了一個(gè)“*”。這樣,就定義了該函數(shù)是返回指向一種基類(lèi)型數(shù)據(jù)的指針。從C語(yǔ)言運(yùn)算符優(yōu)先級(jí)來(lái)講,因()優(yōu)先級(jí)高于*,所以數(shù)組名先與()結(jié)合,顯然這是函數(shù)形式。函數(shù)前面有一個(gè)*,表示此函數(shù)是指針型函數(shù)(函數(shù)值是指針)。最前面的基類(lèi)型是返回的指針?biāo)赶驍?shù)據(jù)的類(lèi)型。
例8.19編程實(shí)現(xiàn)一個(gè)班學(xué)生課程成績(jī)的查詢(xún)。要求輸入一個(gè)學(xué)生的序號(hào)后,能輸出該學(xué)生全部課程的成績(jī)。用指針型函數(shù)來(lái)實(shí)現(xiàn)。
編程思路:一個(gè)班學(xué)生成績(jī)表用二維數(shù)組來(lái)表示。定義一個(gè)查詢(xún)函數(shù),根據(jù)學(xué)生序號(hào),確定一個(gè)指向該學(xué)生各門(mén)課成績(jī)行的指針,此指針作為函數(shù)返回值。在主函數(shù)中,通過(guò)返回指針可輸出該學(xué)生的各門(mén)課成績(jī)。為簡(jiǎn)化數(shù)據(jù)而突出程序的設(shè)計(jì)方法,假設(shè)一個(gè)班有3個(gè)學(xué)生、4門(mén)課成績(jī)。
分析:函數(shù)search定義為指針型函數(shù),形參pointer是指向包含4個(gè)實(shí)型數(shù)據(jù)元素的一維數(shù)組的指針變量。函數(shù)中,定義pt是指向變量的指針變量。*(pointer+n)是引用第2層邏輯一維數(shù)組的第n個(gè)元素,即得到第n行首元素地址,把此地址賦給變量pt,作為函數(shù)返回值。主函數(shù)中,調(diào)用查詢(xún)函數(shù),將返回指針賦給p,在for循環(huán)中,通過(guò)p輸出所指向?qū)W生的各門(mén)課成績(jī)。請(qǐng)注意程序中所定義的指針變量p、pt和pointer的區(qū)別。
例8.20在一個(gè)班學(xué)生成績(jī)表中,查找不及格課程的學(xué)生,輸出序號(hào)及成績(jī)。
編程思路:在例8.19的基礎(chǔ)上,定義查詢(xún)函數(shù)為查找一個(gè)學(xué)生有無(wú)不及格成績(jī),如有則確定一個(gè)指向該學(xué)生成績(jī)行首的指針;如無(wú),則設(shè)置指針為空。指針作為函數(shù)返回值。在主函數(shù)中,要按學(xué)生序號(hào)逐一調(diào)用查詢(xún)函數(shù),根據(jù)返回指針值判定是否有不及格成績(jī),若有,則通過(guò)返回指針輸出該學(xué)生各門(mén)課成績(jī);若無(wú),則不輸出。
分析:定義函數(shù)search檢查一個(gè)學(xué)生有無(wú)不及格成績(jī)。形參pointer是指向包含4個(gè)實(shí)型數(shù)據(jù)元素的一維數(shù)組的指針變量。函數(shù)中,定義pt是指向?qū)嵭妥兞康闹羔樧兞?。循環(huán)實(shí)現(xiàn)檢查一個(gè)學(xué)生每門(mén)課成績(jī)是否低于60,若是,則將該學(xué)生成績(jī)行首地址賦給pt;若無(wú),則pt為NULL。函數(shù)調(diào)用后,返回pt值。在主函數(shù)中將pt值賦給p,if語(yǔ)句判定p的值是否等于一個(gè)學(xué)生成績(jī)行首地址*(score+i),若是,則該學(xué)生有不及格成績(jī),用for循環(huán)輸出該學(xué)生各門(mén)課成績(jī);若不是,則該學(xué)生無(wú)不及格成績(jī),不輸出。
8.7多重指針與指針數(shù)組
何謂多重指針?怎樣通過(guò)多重指針引用數(shù)據(jù)?在本章開(kāi)頭已經(jīng)介紹,通過(guò)指針變量來(lái)引用另一變量值的方式是間接訪問(wèn)。這屬于“單級(jí)間址”。如果再有一個(gè)指針變量,使其指向目標(biāo)變量的指針變量,就形成了“二級(jí)間址”的方法,即形成了二重指針。從理論上講,間址方法可以延伸到更多的層級(jí),即多重指針。多重指針及其指向關(guān)系如圖8.13所示。在實(shí)際應(yīng)用中,很少使用超過(guò)二級(jí)的間址。間址層級(jí)越多,越難理解,出錯(cuò)機(jī)會(huì)也會(huì)增多。圖8.13多重指針示意圖
8.7.1指針數(shù)組
若一個(gè)數(shù)組的元素均為指針類(lèi)型數(shù)據(jù),則稱(chēng)為指針數(shù)組,也就是說(shuō),指針數(shù)組中每一個(gè)元素都存放一個(gè)地址,相當(dāng)于一個(gè)指針變量。
使用指針數(shù)組也必須先定義。指針數(shù)組定義的一般形式為
基類(lèi)型符*數(shù)組名[數(shù)組長(zhǎng)度];
容易看出,指針數(shù)組與基本數(shù)據(jù)類(lèi)型數(shù)組定義的區(qū)別在于:基類(lèi)型符與數(shù)組名之間加了一個(gè)“*”,表示數(shù)組元素是指針類(lèi)型,這些指針都指向基類(lèi)型數(shù)據(jù)。從C語(yǔ)言運(yùn)算符優(yōu)先級(jí)看,由于[]比*優(yōu)先級(jí)高,因此數(shù)組名先與[數(shù)組長(zhǎng)度]結(jié)合,這顯然是數(shù)組形式,再與*結(jié)合,表示此數(shù)組是指針類(lèi)型。
例如:
int*a[4];
定義了數(shù)組a,包含4個(gè)指針類(lèi)型元素,這些指針元素都指向整型數(shù)據(jù)。
請(qǐng)注意指針數(shù)組定義與指向一維數(shù)組的指針變量定義的區(qū)別。如果寫(xiě)成“int(*a)[4];”,就成為指向一維數(shù)組的指針變量的定義了。
指針數(shù)組最典型的應(yīng)用是:指向若干個(gè)字符串,使字符串處理更加方便靈活。例如,圖書(shū)館有若干本書(shū),把書(shū)名放在一起組成書(shū)目,經(jīng)常需要對(duì)書(shū)目進(jìn)行排序和查詢(xún)。因字符串本身就是一個(gè)字符數(shù)組,存放書(shū)目的多個(gè)字符串需要設(shè)計(jì)一個(gè)二維字符數(shù)組。但在定義二維數(shù)組時(shí),需要確定列數(shù),一旦列數(shù)設(shè)定,二維數(shù)組中每一行包含的元素個(gè)數(shù)就是相等的。實(shí)際上,書(shū)名字符串的長(zhǎng)度一般是不相等的。如果按最長(zhǎng)的字符串來(lái)定義列數(shù),則會(huì)浪費(fèi)許多存儲(chǔ)單元,而且在進(jìn)行字符串處理時(shí)也會(huì)降低效率。
使用指針數(shù)組,可以分別定義一些字符串,用指針數(shù)組的元素分別指向各字符串,如圖8.14所示。這樣能有效解決上述問(wèn)題。
圖8.14多個(gè)字符串的存儲(chǔ)及與指針數(shù)組的指向示意圖
例8.21將書(shū)目字符串按字母順序輸出。
編程思路:定義一個(gè)指針數(shù)組,用各字符串對(duì)其進(jìn)行初始化,即把各字符串首字符地址賦給指針數(shù)組各元素。用選擇法排序,不移動(dòng)字符串,而只改變指針數(shù)組各元素的指向。
分析:定義sort函數(shù)的作用是對(duì)字符串排序。形參name是指針數(shù)組名,接收實(shí)參傳遞的name數(shù)組0行首字符地址,使形參name和實(shí)參name指向同一個(gè)數(shù)組。用選擇法對(duì)字符串排序。strcmp是系統(tǒng)提供的字符串比較函數(shù),name[k]和name[j]是第k個(gè)和第j個(gè)字符串首字符的地址。strcmp(name[k],name[j])的值為:如果name[k]所指向的字符串大于name[j]所指向的字符串,其值為正;若相等,其值為0;若小于,其值為負(fù)。if語(yǔ)句的作用是比較兩個(gè)字符串,并將小串的序號(hào)記錄在變量k中。當(dāng)執(zhí)行完內(nèi)循環(huán)for語(yǔ)句后,第k串最小。若k≠i,就表示最小串不是第i串。故將name[i]和name[k]對(duì)換,也就是將其指向互換。
執(zhí)行完sort函數(shù)后,指針數(shù)組元素的指向如圖8.15
所示。
printf的作用是輸出排好序的字符串。因name[0]~name[4]分別指向從小到大排好序的字符串,所以按指針數(shù)組元素順序,用“%s”格式控制,就能輸出排好序的字符串。圖8.15排序后指針數(shù)組元素的指向
8.7.2指向指針數(shù)據(jù)的指針
指針數(shù)組的存儲(chǔ)同基本類(lèi)型數(shù)組一樣,按元素順序連續(xù)存儲(chǔ)在一個(gè)存儲(chǔ)區(qū),每個(gè)元素都有一個(gè)存儲(chǔ)地址。所以,可定義一個(gè)指向指針數(shù)組元素的指針變量,通過(guò)該指針變量可以引用指針數(shù)組中的元素。這種指針變量稱(chēng)為指向指針數(shù)據(jù)的指針變量。指向指針數(shù)據(jù)的指針變量定義的一般形式為
基類(lèi)型符**變量名;
從C語(yǔ)言運(yùn)算符的優(yōu)先級(jí)來(lái)看,*運(yùn)算符的結(jié)合性是從右到左,因此,“**變量名”相當(dāng)于*(*變量名),顯然“*變量名”是指針變量的定義形式,前面再加一個(gè)“*”,表示該指針變量指向指針型數(shù)據(jù)。
例如:
char**p;
定義了指針變量p是指向一個(gè)字符型指針變量的。
在前面定義書(shū)目指針數(shù)組的基礎(chǔ)上,看下面語(yǔ)句的功能作用:
其中,第1個(gè)printf函數(shù)語(yǔ)句中,“*p”是引用字符型指針數(shù)組元素name[2]的值,輸出的是一個(gè)地址。第2個(gè)printf函數(shù)語(yǔ)句中,“*p”與“%s”配合,輸出的是name[2]所指向的字符串。
例8.22使用指向指針數(shù)據(jù)的指針變量輸出多個(gè)字符串。
編程思路:定義一個(gè)指針數(shù)組,并對(duì)其初始化,使數(shù)組中的每一個(gè)元素分別指向5個(gè)字符串。定義一個(gè)指向指針型數(shù)據(jù)的指針變量,使其先后指向指針數(shù)組各元素,并輸出這些元素所指向的字符串。
分析:在主函數(shù)的開(kāi)頭,定義了指向字符的指針數(shù)組name,同時(shí)用字符串賦初值的方式初始化,分別將5個(gè)字符串的首地址賦給數(shù)組元素。又定義指向指針數(shù)據(jù)的指針變量p,在for循環(huán)中,賦值語(yǔ)句“p=name+i;”使p指向指針數(shù)組的第i個(gè)元素,即第i個(gè)字符串的首地址。在“printf("%s\n",*p);”語(yǔ)句中使用“%s”,輸出p所指向的字符串。
例8.23使用指針數(shù)組元素指向一個(gè)整型數(shù)組的元素,用指向指針數(shù)據(jù)的指針變量輸出整型數(shù)組各元素的值。
分析:主函數(shù)中定義了一個(gè)整型數(shù)組a,又定義了一個(gè)指向整型數(shù)據(jù)的指針數(shù)組,同時(shí)對(duì)其初始化,將5個(gè)整型數(shù)組元素的地址賦給指針數(shù)組元素。在“printf("%d",**p);”語(yǔ)句中,“**p”屬于二重間址訪問(wèn),從p所指向的指針數(shù)組得到數(shù)據(jù)元素的地址,再由元素地址從數(shù)據(jù)數(shù)組中取得元素值輸出。
8.8用于動(dòng)態(tài)內(nèi)存分配的指針型函數(shù)
系統(tǒng)怎樣給程序數(shù)據(jù)分配內(nèi)存空間?怎樣有效地使用內(nèi)存空間?在程序中定義的數(shù)據(jù)對(duì)象由系統(tǒng)分配相應(yīng)的存儲(chǔ)單元。全局?jǐn)?shù)據(jù)對(duì)象被分配在內(nèi)存中的靜態(tài)存儲(chǔ)區(qū),非靜態(tài)的局部變量(包括形參)是分配在內(nèi)存中的動(dòng)態(tài)存儲(chǔ)區(qū)的,這個(gè)存儲(chǔ)區(qū)是一個(gè)稱(chēng)為棧(stack)的區(qū)域。
除此之外,C語(yǔ)言還允許建立內(nèi)存動(dòng)態(tài)分配區(qū)域,用于存放一些臨時(shí)用的數(shù)據(jù),這些數(shù)據(jù)不在程序中定義,也不必等到函數(shù)結(jié)束時(shí)才釋放,而是根據(jù)需要隨時(shí)開(kāi)辟,不需要時(shí)隨時(shí)釋放。這些數(shù)據(jù)臨時(shí)存放在一個(gè)特別的自由存儲(chǔ)區(qū),稱(chēng)為堆(heap)區(qū)。可以根據(jù)需要,向系統(tǒng)申請(qǐng)所需大小的空間。堆區(qū)中的數(shù)據(jù)只能通過(guò)指針來(lái)引用。因?yàn)闆](méi)有在程序中定義,就沒(méi)有數(shù)據(jù)對(duì)象的名稱(chēng),無(wú)法通過(guò)數(shù)據(jù)對(duì)象名來(lái)引用。
8.8.1動(dòng)態(tài)分配內(nèi)存的函數(shù)
對(duì)內(nèi)存的動(dòng)態(tài)分配是通過(guò)系統(tǒng)提供的庫(kù)函數(shù)來(lái)實(shí)現(xiàn)的。下面介紹幾個(gè)主要的庫(kù)函數(shù)。
1.開(kāi)辟動(dòng)態(tài)存儲(chǔ)空間函數(shù)(malloc)
該函數(shù)原型為
void*malloc(unsignedintsize);
其作用是在內(nèi)存的動(dòng)態(tài)存儲(chǔ)區(qū)中分配一個(gè)長(zhǎng)度為size字節(jié)的連續(xù)空間。此函數(shù)是指針型函數(shù),返回值是所分配存儲(chǔ)區(qū)域的起始地址,如果分配未成功,則返回空指針(NULL)。利用返回指針可以使用該存儲(chǔ)區(qū)域中的數(shù)據(jù)。例如:
malloc(100);
開(kāi)辟100個(gè)字節(jié)的動(dòng)態(tài)存儲(chǔ)區(qū),函數(shù)返回該區(qū)域首字節(jié)地址。
2.開(kāi)辟數(shù)組動(dòng)態(tài)存儲(chǔ)空間函數(shù)(calloc)
該函數(shù)原型為
void*calloc(unsignedn,unsignedsize);
其作用是在內(nèi)存的動(dòng)態(tài)存儲(chǔ)區(qū)中分配n個(gè)長(zhǎng)度為size字節(jié)的連續(xù)空間,即可為n個(gè)元素的數(shù)組開(kāi)辟一個(gè)動(dòng)態(tài)存儲(chǔ)區(qū),每個(gè)元素的存儲(chǔ)長(zhǎng)度為size字節(jié)。函數(shù)的返回值是所分配存儲(chǔ)區(qū)域的起始地址,如果分配未成功,則返回空指針(NULL)。利用返回指針可以使用該存儲(chǔ)區(qū)域中的數(shù)據(jù)元素。
例如:
p=calloc(50,4);
開(kāi)辟50?×?4個(gè)字節(jié)的動(dòng)態(tài)存儲(chǔ)區(qū),把該區(qū)域首字節(jié)地址賦給指針變量p。
3.釋放動(dòng)態(tài)存儲(chǔ)空間函數(shù)(free)
該函數(shù)原型為
voidfree(void*p);
其作用是釋放指針變量p所指向的動(dòng)態(tài)存儲(chǔ)空間。p應(yīng)是最近一次調(diào)用malloc或calloc函數(shù)時(shí)得到的函數(shù)返回值。該函數(shù)無(wú)返回值。例如:
free(p);
即釋放指針變量p所指向的已分配的動(dòng)態(tài)存儲(chǔ)空間。
4.改變已分配動(dòng)態(tài)存儲(chǔ)空間大小函數(shù)(realloc)
該函數(shù)原型為
void*realloc(void*p,unsignedintsize);
其作用是對(duì)已經(jīng)通過(guò)malloc或calloc函數(shù)獲得的動(dòng)態(tài)存儲(chǔ)空間,改變其大小,將p所指向的動(dòng)態(tài)存儲(chǔ)空間的大小改變?yōu)閟ize,即可為n個(gè)元素的數(shù)組開(kāi)辟一個(gè)動(dòng)態(tài)存儲(chǔ)區(qū),每個(gè)元素的存儲(chǔ)長(zhǎng)度為size字節(jié)。p的值不變,如果重新分配不成功,則返回空指針(NULL)。
例如:
realloc(p,50);
將p所指向的已分配的動(dòng)態(tài)存儲(chǔ)空間改變?yōu)?0字節(jié)。
以上4個(gè)函數(shù)的聲明在stdlib.h頭文件中,在用到這些函數(shù)時(shí),應(yīng)當(dāng)使用#include<stdlib.h>預(yù)處理指令把stdlib.h頭文件包含到程序文件中。
8.8.2void指針類(lèi)型
C99標(biāo)準(zhǔn)允許使用基類(lèi)型為void的指針類(lèi)型。可以定義一個(gè)基類(lèi)型為void的指針變量(即void*指針變量名),這種類(lèi)型的指針變量不指向任何類(lèi)型的數(shù)據(jù),可以理解為指向空類(lèi)型或不指向確定類(lèi)型的數(shù)據(jù)。定義這種指針變量有何意義呢?其意義在于,當(dāng)把void指針賦給不同類(lèi)型的指針變量時(shí),或把不同類(lèi)型的指針賦給void指針變量時(shí),編譯系統(tǒng)會(huì)自動(dòng)進(jìn)行轉(zhuǎn)換,而不需用戶(hù)進(jìn)行強(qiáng)制轉(zhuǎn)換,使之適合被賦值的變量的類(lèi)型。
例如:
上述語(yǔ)句中,“p3=p1;”和“p2=p3;”都是合法的。雖然類(lèi)型不同,但編譯系統(tǒng)會(huì)自動(dòng)進(jìn)行轉(zhuǎn)換,使之適合被賦值的變量的類(lèi)型?!皃rintf("%d",
溫馨提示
- 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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 康復(fù)護(hù)理質(zhì)量目標(biāo)管理
- 大班安全別爬窗戶(hù)
- 普通心理學(xué)(第2版)課件 第十章 需要與動(dòng)機(jī)
- 形位公差檢測(cè)規(guī)定
- 護(hù)理教育導(dǎo)論課程框架
- 混凝土結(jié)構(gòu)工程施工規(guī)范培訓(xùn)課件
- 婚慶禮儀培訓(xùn)實(shí)務(wù)要點(diǎn)
- 護(hù)理案例分析課件
- 統(tǒng)編版語(yǔ)文一年級(jí)下冊(cè)14文具的家 課件
- 2025年單位保安年終工作總結(jié)模版
- JJF 2119-2024低氣壓試驗(yàn)箱校準(zhǔn)規(guī)范
- SH/T 3533-2024 石油化工給水排水管道工程施工及驗(yàn)收規(guī)范(正式版)
- 期末考試-公共財(cái)政概論-章節(jié)習(xí)題
- 高中物理課件:Tracker軟件在高中物理實(shí)驗(yàn)教學(xué)中的應(yīng)用-
- 英語(yǔ)詞匯學(xué)術(shù)語(yǔ)表
- 維保服務(wù)方案及維保體系
- YUM-百勝-危機(jī)管理培訓(xùn)課程
- 大數(shù)據(jù)在化工行業(yè)中的應(yīng)用與創(chuàng)新
- DB32/T 4700-2024 蓄熱式焚燒爐系統(tǒng)安全技術(shù)要求
- 施今墨對(duì)藥臨床經(jīng)驗(yàn)集
- 自動(dòng)控制原理課件8狀態(tài)空間分析法
評(píng)論
0/150
提交評(píng)論