《C語言程序設(shè)計(jì)基礎(chǔ)》課件第8章_第1頁
《C語言程序設(shè)計(jì)基礎(chǔ)》課件第8章_第2頁
《C語言程序設(shè)計(jì)基礎(chǔ)》課件第8章_第3頁
《C語言程序設(shè)計(jì)基礎(chǔ)》課件第8章_第4頁
《C語言程序設(shè)計(jì)基礎(chǔ)》課件第8章_第5頁
已閱讀5頁,還剩199頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

第8章

指針——對(duì)存儲(chǔ)信息的引用機(jī)制8.1指針的概念8.2通過指針引用變量的值8.3通過指針引用一維數(shù)組8.4通過指針引用二維數(shù)組8.5通過指針引用字符串8.6通過指針調(diào)用函數(shù)8.7多重指針與指針數(shù)組8.8用于動(dòng)態(tài)內(nèi)存分配的指針型函數(shù)實(shí)訓(xùn)任務(wù)十五

熟悉指針數(shù)據(jù)類型,掌握指針的正確使用實(shí)訓(xùn)任務(wù)十六

學(xué)習(xí)指針的應(yīng)用

8.1指

指針是什么?為什么通過指針可以訪問存儲(chǔ)器中的信息?

指針的一般意義大家都不會(huì)陌生,那就是建立一種指向,如儀器儀表的指針,路標(biāo)指針等。C語言指針也是建立一種指向,只是指向的是存儲(chǔ)器單元,按其所指訪問存儲(chǔ)器單元中的信息。要理解C語言中指針的內(nèi)涵,需了解對(duì)存儲(chǔ)器的訪問機(jī)制。計(jì)算機(jī)對(duì)存儲(chǔ)器是按地址進(jìn)行訪問的。存儲(chǔ)器包含大量的信息單元,為能方便地訪問需要的信息單元,每一個(gè)單元都有一個(gè)編號(hào),這個(gè)編號(hào)稱為地址。要訪問某一單元,只要給出該單元地址,就能準(zhǔn)確引用該單元信息。打個(gè)通俗的比方,一個(gè)樓房包含許多房間,為能方便地尋找某個(gè)房間,給每一個(gè)房間編一個(gè)號(hào)碼,知道了房號(hào)就能準(zhǔn)確地找到這個(gè)房間。由此可知,地址就是對(duì)存儲(chǔ)器單元的指向。即給出地址就能訪問存儲(chǔ)器單元,訪問存儲(chǔ)器單元必須有地址。C語言中的指針就是存儲(chǔ)器單元的地址,也可以反過來說,地址就是存儲(chǔ)器單元的指針。簡言之,指針就是地址,地址就是指針。因此,指針是對(duì)存儲(chǔ)器單元信息的一種引用機(jī)制。

地址屬于一個(gè)與存儲(chǔ)器硬件特性相關(guān)的概念。在高級(jí)語言中,編程人員不涉及計(jì)算機(jī)的硬件特性,對(duì)硬件資源的分配與處理由編譯系統(tǒng)來完成。在前面學(xué)習(xí)變量、數(shù)組、函數(shù)時(shí),僅是按語言規(guī)定進(jìn)行定義,按所定義的符號(hào)名稱進(jìn)行引用,未涉及按地址引用的問題。事實(shí)上,程序在編譯時(shí),系統(tǒng)給定義的數(shù)據(jù)對(duì)象或函數(shù)都要分配相應(yīng)的存儲(chǔ)單元,符號(hào)名稱也具有相應(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ù)引用示意圖對(duì)存儲(chǔ)器單元的訪問有兩種方式:一種是直接訪問,另一種是間接訪問。

直接訪問是直接引用地址所指向單元中的內(nèi)容。按變量名引用變量的值屬于直接訪問。

間接訪問是將欲訪問變量的地址存放在另一個(gè)變量中,先通過該變量取得欲訪問變量的地址,再按這個(gè)地址引用變量的值。打個(gè)通俗的比方,從一個(gè)抽屜中存取東西,人們也可采取兩種方式:一種是將一個(gè)抽屜A的鑰匙帶在身上,需要時(shí)直接用A抽屜鑰匙,打開抽屜,存取所需東西;另一種是將A抽屜鑰匙放在另一抽屜B中,鎖起來,需要打開A抽屜時(shí),先要用B鑰匙打開B抽屜,取出A鑰匙后,再打開A抽屜,才能存取其中的東西。這就是一個(gè)間接訪問的過程。例如,我們將i變量所標(biāo)識(shí)的存儲(chǔ)單元的地址(1000)存放在i_pointer變量中,現(xiàn)要引用i變量的值,先要訪問i_pointer變量,從中取得i變量的地址,然后才能按此地址所指向的i變量取得所需要的值。訪問過程如圖8.1所示。

為了實(shí)現(xiàn)一個(gè)變量的間接訪問,需引入另外一種變量,稱之為指針變量。如果一個(gè)變量專門用來存放另一個(gè)變量的地址(即指針),則稱這個(gè)變量為指針變量。也就是說,指針變量的值是地址(即指針)。

從存儲(chǔ)器的訪問機(jī)制,我們引入了“指針”和“指針變量”,弄清楚這兩個(gè)概念的內(nèi)涵,對(duì)學(xué)習(xí)本章后續(xù)內(nèi)容是至關(guān)重要的。

8.2通過指針引用變量的值

8.2.1指針變量的定義與初始化

在程序中怎樣使用指針變量?

指針變量是有別于普通變量的,也必須先定義后使用。

指針變量定義的一般形式為

基類型符*指針變量名;

其中,基類型符是基本數(shù)據(jù)類型關(guān)鍵字,如int、float、char、double等,用來指定所定義指針變量可以指向的變量類型。簡單地說,就是指針變量所指的數(shù)據(jù)對(duì)象的類型?!?”規(guī)定了所定義的變量是指針型變量。反過來說,沒有此星號(hào),那就成為普通變量的定義了。

指針變量名是所定義的指針變量的標(biāo)識(shí)符,其命名規(guī)則同普通變量定義中的變量名一樣。

注:*和指針變量名之間可以有空格,也可以沒有空格,兩者均可。

下面是指針變量定義的幾個(gè)例子:

int*pi1;

語句定義了一個(gè)指針變量pi1,它只可以指向整型變量。

char*pc1,*pc2;

語句定義了兩個(gè)指針變量pc1和pc2,它們只可以指向字符型變量。

float*pf1,*pf2,*pf3;

語句定義了三個(gè)指針變量pf1、pf2和pf3,它們只可以指向?qū)嵭妥兞俊?/p>

從以上舉例可以看出,一個(gè)定義語句可以定義一個(gè)指針變量,也可以定義多個(gè)指針變量,但只可以是同一類型的變量。定義了指針變量,僅聲明了所定義的變量是指針類型和可指向的變量類型,并沒有確定的指向。也就是說,沒有具體指向哪一個(gè)變量或存儲(chǔ)器單元。打個(gè)通俗的比方,我們加工出一些指針,在沒有安裝到某一個(gè)儀器儀表前,它們沒有具體的指向。要使所定義的指針變量指向某一變量,必須給指針變量初始化,將所要指向變量的地址賦給指針變量。指針變量中只能存放地址(即指針)。若要給指針變量賦數(shù)值則毫無意義。

指針變量初始化可有兩種方式:一是先定義指針變量,然后進(jìn)行初始化;二是定義指針變量的同時(shí)初始化。

8.2.2指針變量的引用

通過指針變量怎樣間接引用變量的值?

在指針變量的引用中要使用兩個(gè)運(yùn)算符?&?和?*?!?”是取地址運(yùn)算符,例如&a是取變量a的地址;“*”是間接引用運(yùn)算符,其后跟指針變量,是取該指針變量所指向的數(shù)據(jù),例如*p表示指針變量p所指向的數(shù)據(jù)。

指針變量有兩種引用:一是通過指針變量引用所指向變量的值,也就是說,通過指針變量實(shí)現(xiàn)變量值的間接引用;二是指針變量是變量,其值也可以引用,只不過引用的是地址(指針)值。下面通過兩個(gè)例子來說明兩種引用和指針變量的應(yīng)用。

例8.1

分析下面程序的運(yùn)行結(jié)果:

運(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利用指針方法實(shí)現(xiàn):輸入兩個(gè)整數(shù),按先大后小的順序輸出。

編程思路:輸入兩個(gè)整數(shù),分別賦給兩個(gè)變量,比較兩個(gè)變量的值,利用指針變量可以不交換兩個(gè)變量的值,而只交換兩個(gè)指針變量的值(即改變指向)。

運(yùn)行結(jié)果:

分析:輸入值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語句中采用了兩個(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ù)中按指針的指向可得到變量的值。下面通過一個(gè)例子來說明指針變量作函數(shù)的參數(shù),指針傳遞的過程。

例8.3例8.2(輸入兩個(gè)整數(shù),按先大后小的順序輸出)中,用指針變量作參數(shù)的函數(shù)來實(shí)現(xiàn),并分析參數(shù)傳遞及其處理過程。

編程思路:用一個(gè)子函數(shù)實(shí)現(xiàn)交換兩個(gè)變量的值,主函數(shù)中判斷兩數(shù)大小,需要交換時(shí)調(diào)用子函數(shù),指針變量作函數(shù)參數(shù),傳遞指針。

分析:程序運(yùn)行先執(zhí)行主函數(shù),輸入2、8分別賦給變量a、b,兩個(gè)指針賦值語句使兩個(gè)指針變量分別指向變量a、b,如圖8.3(a)所示。執(zhí)行if語句,判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ù)傳遞示意圖通過本例應(yīng)該深刻理解,指針變量作函數(shù)參數(shù),實(shí)參傳遞地址(指針)給形參,使對(duì)應(yīng)的實(shí)參指針變量和形參指針變量指向同一個(gè)變量(存儲(chǔ)單元),在被調(diào)函數(shù)中通過形參指針變量對(duì)所指向的變量(存儲(chǔ)單元)的操作結(jié)果,在主函數(shù)中引用。相當(dāng)于通過被操作的變量(存儲(chǔ)單元)傳送了值。這樣就給函數(shù)間增加了一種數(shù)據(jù)傳遞的渠道。需特別注意:通過實(shí)參和形參所指向變量值的傳遞是雙向的(在主調(diào)函數(shù)和被調(diào)函數(shù)中都可以引用變量的值),但實(shí)參指針變量向形參指針變量傳遞地址值仍是單向傳遞。也就是說,形參值不可能傳遞給實(shí)參。因?yàn)楹瘮?shù)調(diào)用結(jié)束后,形參就不復(fù)存在了。變量作函數(shù)參數(shù),函數(shù)調(diào)用只能得到一個(gè)返回值(即函數(shù)值),使用指針變量作函數(shù)參數(shù),可以得到多個(gè)操作變量的值。如果想通過函數(shù)調(diào)用得到n個(gè)操作數(shù)據(jù),只需在函數(shù)中設(shè)置n個(gè)指針變量參數(shù),函數(shù)調(diào)用時(shí),將n個(gè)變量的指針傳遞給形參,在被調(diào)函數(shù)中通過形參指針變量對(duì)所指向變量進(jìn)行操作,在主調(diào)函數(shù)中就可得到操作結(jié)果。下面通過一個(gè)例子來說明。

例8.4輸入3個(gè)整數(shù),按由大到小的順序輸出。

編程思路:對(duì)3個(gè)數(shù)排序,必須兩兩比較,根據(jù)大小關(guān)系進(jìn)行交換。這樣,需要3次比較,可能需要3次兩數(shù)的交換操作。為避免交換操作的重復(fù)書寫,可設(shè)計(jì)兩個(gè)函數(shù),一個(gè)函數(shù)實(shí)現(xiàn)兩數(shù)的交換功能,一個(gè)函數(shù)進(jìn)行兩數(shù)比較,根據(jù)判定結(jié)果調(diào)用交換函數(shù)。

運(yùn)行結(jié)果:

分析:在主函數(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)中,通過形參指針變量的間接引用來比較a與b、a與c、b與c,如果前者小于后者,調(diào)用swap函數(shù),將對(duì)應(yīng)變量的地址傳遞給形參指針變量pk1、pk2。在swap(int*pk1,int*pk2)中利用形參指針變量的間接引用來實(shí)現(xiàn)所指向變量值的交換。

8.3通過指針引用一維數(shù)組

8.3.1一維數(shù)組的存儲(chǔ)結(jié)構(gòu)與指針

如何建立數(shù)組元素與其指針的對(duì)應(yīng)關(guān)系?

一維數(shù)組的存儲(chǔ)是:系統(tǒng)根據(jù)所定義的類型,按元素順序分配一個(gè)連續(xù)的存儲(chǔ)空間。類型規(guī)定了數(shù)組元素的存儲(chǔ)長度,即一個(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é)。例如,有語句“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語言中,可以不使用存儲(chǔ)地址來引用數(shù)組元素,而以數(shù)組名代表數(shù)組存儲(chǔ)首地址(指針),以數(shù)組元素的引用符號(hào)取地址運(yùn)算符代表元素地址(指針)。如a表示所定義數(shù)組首地址,&a[0]表示第0個(gè)元素地址,&a[1]表示第1個(gè)元素地址,…

知道數(shù)組及數(shù)組元素指針后,就可以定義指針變量來實(shí)現(xiàn)數(shù)組元素的間接引用。例如:

“p=a;”可以置換成“p=&a[0];”,這兩個(gè)語句是等效的,都使指針變量p指向所定義數(shù)組的首元素。注意,數(shù)組名就表示數(shù)組首地址,一個(gè)數(shù)組元素相當(dāng)于一個(gè)變量,必須用“&”才得到元素地址。

定義了指向數(shù)組的指針變量后,就可以利用指針變量及其相關(guān)運(yùn)算,以方便靈活的方法引用數(shù)組元素。

8.3.2一維數(shù)組指針調(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)整作用類似于加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是沒有意義的。

5.p2-p1運(yùn)算的意義

如果兩個(gè)指針變量p1、p2都指向同一數(shù)組,p2>p1,則p2-p1是兩個(gè)指針之間的元素個(gè)數(shù)。例如:8.3.3通過指針引用數(shù)組元素

例8.5一維數(shù)組的輸入與輸出。

下面用4種數(shù)組元素的引用方法編寫程序,請(qǐng)認(rèn)真分析并體會(huì)數(shù)組元素的引用方法與編程技巧。

(1)下標(biāo)法。

運(yùn)行結(jié)果同下標(biāo)法。程序中使用了數(shù)組元素指針a+i和指針法引用數(shù)組元素:*(a+i)。

思考:將*(p++)改為*(p+i),影響程序的正確性嗎?兩個(gè)“p=a;”語句的作用是什么?如果去掉第2個(gè)“p=a;”語句,程序的執(zhí)行結(jié)果會(huì)正確嗎?為什么?

(4)指針變量法(使用p++和*(--p))。

運(yùn)行結(jié)果:

顯然,程序運(yùn)行結(jié)果以逆序輸出。這是因?yàn)?,循環(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ù)組的指針變量兩種形式傳遞。實(shí)參可以是數(shù)組名或指針變量,形參是接收實(shí)參傳遞的數(shù)組首元素地址的,應(yīng)該是指針變量。但因C語言程序編譯時(shí),是把形參數(shù)組名作為指針處理的,這樣,形參也可以是數(shù)組名。數(shù)組指針作函數(shù)參數(shù),實(shí)參與形參可有表8.1列出的組合關(guān)系。表8.1函數(shù)實(shí)參與形參的組合關(guān)系下面通過例子來說明一維數(shù)組指針作函數(shù)參數(shù)的程序設(shè)計(jì)方法。

例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)系圖運(yùn)行結(jié)果:

分析:在定義函數(shù)inv時(shí),形參是數(shù)組名x,形參數(shù)組沒有定義大小,用n來接收數(shù)組元素個(gè)數(shù)。因?yàn)榫幾g系統(tǒng)把形參數(shù)組名作為一個(gè)指針變量處理,并不開辟數(shù)組空間。在主函數(shù)中,函數(shù)調(diào)用語句“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í)慣這種直觀的引用方法。

運(yùn)行結(jié)果同(1)。在定義函數(shù)inv時(shí),指針變量作形參。引用形參數(shù)組時(shí),要用指針變量法。在inv函數(shù)中,定義了3個(gè)指針變量,i、j是指向當(dāng)前交換的前元素和后元素的指針變量,p是指向終止交換操作元素的指針變量。函數(shù)調(diào)用過程及參數(shù)傳遞同(1)。

(3)用一個(gè)函數(shù)來實(shí)現(xiàn)交換,實(shí)參用指針變量,形參用數(shù)組名。

函數(shù)運(yùn)行結(jié)果同(1)。與(1)對(duì)比,只是在主函數(shù)中定義了指針變量,使其指向數(shù)組首地址,作函數(shù)的實(shí)參。

(4)用一個(gè)函數(shù)來實(shí)現(xiàn)交換,實(shí)參用指針變量,形參用指針變量。運(yùn)行結(jié)果同(1)。與(1)、(2)、(3)比較,很容易找到異同,請(qǐng)讀者自行分析。

上面通過一個(gè)問題,用函數(shù)指針作參數(shù)的4種組合關(guān)系來實(shí)現(xiàn),通過對(duì)比,可反映各自特點(diǎn),并揭示程序設(shè)計(jì)的方法與技巧。

例8.8

用指針法對(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)系中的任一種。下面通過實(shí)參是指針變量、形參是數(shù)組名來實(shí)現(xiàn)。運(yùn)行結(jié)果:

分析:sort函數(shù)中用數(shù)組名作形參,用“下標(biāo)法”引用形參數(shù)組元素。如果形參改為指針變量,函數(shù)中也可以用與“下標(biāo)法”類似的形式引用形參數(shù)組元素,可采用x+i和x+j作指針來引用數(shù)組元素。

8.4通過指針引用二維數(shù)組

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)來存儲(chǔ)。二維數(shù)組是按行依次線性展開存儲(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行元素。圖8.7二維數(shù)組存儲(chǔ)結(jié)構(gòu)示意圖設(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)

通過指針對(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語言程序中,指向數(shù)組元素的指針是邏輯指針,并不是物理存儲(chǔ)地址。

根據(jù)二維數(shù)組的線性存儲(chǔ)結(jié)構(gòu),二維數(shù)組可以看成由一維行數(shù)組組成的一維數(shù)組,如圖8.8所示。

圖8.8二維數(shù)組一維行數(shù)組組成示意圖可以把二維數(shù)組看成由兩層一維數(shù)組組成。第一層是每一行的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)的問題,可以按一維數(shù)組元素的引用方法來分析二維數(shù)組元素的引用。從一維數(shù)組元素的指針來看,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]。這正是問題理解的關(guān)鍵。我們可以從邏輯引用的角度來分析,*(a+0)=a[0],*(a[0]+0)=a[0][0]。為幫助理解,又引用本章前所打比方:從B抽屜取東西,將B鑰匙放在A抽屜,隨身帶A抽屜鑰匙,用A鑰匙打開A抽屜,取出B鑰匙,再用B鑰匙打開B抽屜?,F(xiàn)在改為B抽屜鑰匙放在桌面上,只指示從桌面上拿到B鑰匙就可打開B抽屜,即不需要用A鑰匙打開A抽屜來得到B鑰匙,如圖8.9所示。也就是說,從第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ù)組元素的指針變量定義。其定義的一般形式為

基類型符(*指針變量名)[m];其中,m表示行一維數(shù)組元素個(gè)數(shù),即二維數(shù)組的列數(shù);基類型符是二維數(shù)組元素的類型。這樣定義,表示所定義的指針變量指向包含m個(gè)基類型元素的一維數(shù)組,即指向邏輯層上的行一維數(shù)組。注意,指針變量兩側(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)過*(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通過指針引用二維數(shù)組元素

1.通過指向元素的指針變量引用二維數(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è)普通變量,通過定義指向變量的指針變量來間接引用數(shù)組元素。

2.通過指向一維數(shù)組的指針變量引用二維數(shù)組元素

設(shè)定義了二維數(shù)組a,并定義了指向一維數(shù)組的指針變量p,則對(duì)數(shù)組元素的應(yīng)用形式為

*(*(p?+?i)?+?j)

若對(duì)p賦首元素指針,則引用a[i][j]元素;若p非首元素地址,則引用p當(dāng)前所指元素后的第i?×?m?+?j個(gè)元素。這種引用方式是把二維數(shù)組看作兩層一維數(shù)組來引用的。先定義指向行一維數(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)程序來實(shí)現(xiàn);也可結(jié)合邏輯結(jié)構(gòu)的行、列下標(biāo),對(duì)指針變量進(jìn)行運(yùn)算調(diào)整來引用任一元素,可用雙重循環(huán)程序結(jié)構(gòu)來實(shí)現(xiàn)。

(1)按存儲(chǔ)結(jié)構(gòu)引用元素。

運(yùn)行結(jié)果:

分析:a數(shù)組包含12個(gè)元素,循環(huán)變量i從0到11變化,(p+i)就依次指向每一個(gè)元素。

(2)結(jié)合邏輯結(jié)構(gòu)下標(biāo),對(duì)指針調(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ù)組的每一元素。外循環(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ù)組任一行任一列元素的值。

編程思路:由鍵盤輸入數(shù)組元素的行、列號(hào),根據(jù)行、列號(hào),使指針指向該元素輸出??墒褂弥赶蛐幸痪S數(shù)組的指針變量。

運(yùn)行結(jié)果:

分析:定義了指向由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ì)不一樣呢?問題的關(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)語句“int(*p)[4];”定義了p是指向行一維數(shù)組的指針,&a[0][0]表示二維數(shù)組首元素地址,“p=&a[0][0];”使p指向了二維數(shù)組元素,所以出現(xiàn)指向類型錯(cuò)誤。

8.4.3二維數(shù)組指針作函數(shù)參數(shù)

例8.11一個(gè)班有n個(gè)學(xué)生,開設(shè)m門課程,計(jì)算總平均分?jǐn)?shù),并輸出任一學(xué)生各門課成績。

編程思路:

(1)為了突出程序設(shè)計(jì)方法和程序結(jié)構(gòu),而不被眾多的數(shù)據(jù)干擾,先設(shè)有3個(gè)學(xué)生、4門課程。在此基礎(chǔ)上,只要添加數(shù)據(jù),設(shè)置相應(yīng)變量的初值,就能實(shí)現(xiàn)更多學(xué)生、更多課程的處理。

(2)班級(jí)成績表顯然屬于二維數(shù)據(jù)表格,按功能設(shè)計(jì)處理函數(shù),按求總平均分?jǐn)?shù)設(shè)計(jì)一個(gè)處理函數(shù),按輸出一個(gè)學(xué)生各門課成績?cè)O(shè)計(jì)一個(gè)處理函數(shù),數(shù)組指針作函數(shù)參數(shù),減少數(shù)據(jù)傳遞數(shù)量,提高程序效率。

運(yùn)行結(jié)果:

分析:

(1)求平均分?jǐn)?shù)函數(shù)average定義中,第一個(gè)形參p定義為指向?qū)嵭蛿?shù)組元素的指針,第2個(gè)形參n是二維數(shù)組元素的個(gè)數(shù),即全班學(xué)生所有課程的成績數(shù)目。函數(shù)調(diào)用時(shí),第1個(gè)實(shí)參*score,是第2層邏輯一維數(shù)組首元素的引用,即score[0],也就是&score[0][0],即score[0][0]的地址,使形參p指向成績數(shù)組首元素。

(2)查找并輸出任一個(gè)學(xué)生成績函數(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門課程成績。*(*(p+n)+i)表示第n個(gè)學(xué)生的第i門課程分?jǐn)?shù),即元素score[n][i]的值。

特別強(qiáng)調(diào):實(shí)參和形參如果是指針類型,應(yīng)當(dāng)注意它們類型的一致性。不能把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ǔ)上,查找有一門以上課程不及格的學(xué)生,并輸出這些學(xué)生全部課程的成績。運(yùn)行結(jié)果:

分析: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,則通過一個(gè)循環(huán)輸出該學(xué)生的號(hào)碼和各門課程成績。函數(shù)調(diào)用時(shí),傳遞實(shí)參score,即score[0]的地址,使形參p指向score數(shù)組的第0行。

8.5通過指針引用字符串

8.5.1字符串的存儲(chǔ)結(jié)構(gòu)與指針

如何建立字符串中字符與其指針的對(duì)應(yīng)關(guān)系?

在字符數(shù)組一節(jié)中已經(jīng)介紹過,字符串是按字符數(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通過指針引用字符串

1.通過數(shù)組名(指針)引用

在字符數(shù)組一節(jié)中已經(jīng)介紹,通過字符數(shù)組名可以引用字符串,使用“%s”格式描述符,可以整體輸入或輸出,只不過沒有引入指針的概念。字符數(shù)組名就表示字符串指針,通過指針可以對(duì)字符串及字符串中的字符進(jìn)行引用。下面通過一個(gè)例子說明引用方法。

例8.13定義一個(gè)字符數(shù)組,存放字符串“Ilovechina!”,輸出該字符串和第8個(gè)元素。

分析:在“printf("%s\n",string);”中使用%s格式描述符和數(shù)組名(指針)對(duì)字符串整體輸出,這里與數(shù)值型數(shù)組不同。在C語言中,“%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開始,實(shí)際上就是串中第8個(gè)字符。

2.通過字符串指針變量引用

指向字符串的指針變量,也就是指向字符數(shù)組的指針變量。指向字符數(shù)組的指針變量也必須先定義后使用,而且指針變量只有賦初值后才能建立指向。

字符型指針變量定義的一般形式為

char*指針變量名;

其中,char屬于基類型,規(guī)定了指針變量指向字符型數(shù)據(jù)(即指向字符串),其他部分同指針變量定義的意義一樣。

給字符型指針變量賦初值的方式也有兩種:一是定義的同時(shí)賦初值;二是先定義,再賦初值。

分析:與例8.13對(duì)比,只是定義了字符型指針變量,用指針變量來引用字符串和字符串中的字符。運(yùn)行結(jié)果也完全一樣。但仍要注意,用數(shù)組名作指針,其地址值是固定的,而用字符型指針變量,其值是可以變化的。在本例中體現(xiàn)得不是太明顯,但在比較復(fù)雜的問題中就會(huì)表現(xiàn)出來。

例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)。下面分別通過指針引用法和指針變量引用法來編寫程序。運(yùn)行結(jié)果:

分析:

①數(shù)組b的長度應(yīng)大于等于數(shù)組a,否則無法完整復(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)控制變量,使兩個(gè)p1、p2同步移動(dòng)。這也是指針變量使用的一種技巧。兩字符串的輸出仍采用了數(shù)組名(指針法),體現(xiàn)了通過指針引用字符串的多種方法。8.5.3字符指針作函數(shù)參數(shù)

例8.16用函數(shù)調(diào)用實(shí)現(xiàn)字符串的復(fù)制。

編程思路:定義一個(gè)函數(shù)來實(shí)現(xiàn)字符串復(fù)制的功能,在主函數(shù)中提供源串,調(diào)用復(fù)制函數(shù)后,輸出源串和目標(biāo)串。函數(shù)參數(shù)可以是字符數(shù)組名(字符串指針)或字符指針變量。下面分別給出實(shí)參和形參4種組合關(guān)系的程序,以供分析比較,體會(huì)編程方法與技巧。

(1)實(shí)參、形參都用字符數(shù)組名(指針)。

運(yùn)行結(jié)果:

分析:程序中函數(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ù)組原來長度大于a數(shù)組,在a數(shù)組復(fù)制到b數(shù)組后,未能全部覆蓋b數(shù)組原有內(nèi)容。b數(shù)組最后3個(gè)元素仍保留原狀。在輸出b時(shí),圖8.11函數(shù)調(diào)用前后的字符數(shù)組狀態(tài)由于按%s格式輸出,遇到“\0”即結(jié)束,因此第1個(gè)“\0”后的字符不輸出。如果采用%c格式,則后面的字符是可以輸出的。

(2)實(shí)參用字符數(shù)組名,形參用字符指針變量。分析:程序的運(yùn)行結(jié)果同(1)。形參是指針變量,調(diào)用時(shí)接收參數(shù)傳遞的源串和目標(biāo)串的首地址,利用指針變量加1調(diào)整運(yùn)算,能很方便地使用for循環(huán)來實(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ù)制。

分析:程序的運(yùn)行結(jié)果同(1)。參數(shù)傳遞及函數(shù)調(diào)用過程請(qǐng)讀者自行分析。

字符型指針作函數(shù)參數(shù),引用字符串元素的方法有多種,給程序設(shè)計(jì)帶來許多技巧。下面通過對(duì)copy_string函數(shù)的改寫來說明程序設(shè)計(jì)的方法與技巧。

對(duì)copy_string函數(shù)可有如下一些改寫,都能保持功能不變。

8.6通過指針調(diào)用函數(shù)

8.6.1函數(shù)指針與指針變量的定義

何謂函數(shù)指針?在程序中如何使用函數(shù)指針或函數(shù)指針變量?

函數(shù)的指針就是函數(shù)代碼存儲(chǔ)的起始地址,也就是函數(shù)調(diào)用時(shí)的入口地址??梢酝ㄟ^指向函數(shù)入口的指針變量(或簡稱指向函數(shù)的指針變量)來調(diào)用函數(shù)。

指向函數(shù)的指針變量又是一種新類型的指針變量,也必須先定義后使用。指向函數(shù)的指針變量定義的一般形式為

基類型符(*變量名)(函數(shù)參數(shù)表列)其中,基類型符是指函數(shù)返回值的類型。變量名前加*表示是指針變量,兩側(cè)再加括號(hào),連同后面的括號(hào)內(nèi)的函數(shù)參數(shù)表列一起,表示所定義的變量是指向返回值為基類型的函數(shù)的指針變量。也就是說,變量名兩側(cè)的括號(hào)和其后的括號(hào)與參數(shù)列表都不能遺漏。

例如:

int(*p)(int,int);

定義了指向返回值為整型、有兩個(gè)整型參數(shù)的函數(shù)的指針變量p。與一般指針變量定義和使用一樣,定義了一個(gè)指向函數(shù)的指針變量后,并沒有指向哪一個(gè)函數(shù),只有當(dāng)賦初值后,才能建立起明確的指向。如果把一個(gè)已定義的函數(shù)名賦給已定義的函數(shù)的指針變量,則使指針變量與函數(shù)建立了明確的指向。8.6.2通過函數(shù)指針調(diào)用函數(shù)

如何通過函數(shù)指針或指針變量調(diào)用函數(shù)?

通過函數(shù)指針調(diào)用函數(shù)有兩種方式:一是通過函數(shù)指針(函數(shù)名);二是通過指向函數(shù)的指針變量。通過函數(shù)名調(diào)用已經(jīng)介紹過,因函數(shù)名僅表示一個(gè)固定地址,故只能調(diào)用一個(gè)固定的函數(shù)。指向函數(shù)的指針變量的值可以變化,可使其指向不同的函數(shù),分別實(shí)現(xiàn)不同函數(shù)的調(diào)用。

例8.17用函數(shù)求兩整數(shù)中的大數(shù)。

編程思路:定義一個(gè)函數(shù),通過比較求出兩個(gè)整數(shù)中的大數(shù)。在主函數(shù)中定義指向函數(shù)的指針變量,通過指針變量調(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)用過程及參數(shù)傳遞同函數(shù)名調(diào)用。如果將“c=(*p)(a,b);”置換為“c=max(a,b);”且不需定義指向函數(shù)的指針變量和賦初值語句,程序的執(zhí)行結(jié)果完全一樣。這個(gè)例子說明了通過指向函數(shù)的指針變量調(diào)用函數(shù)的方法,但看不出通過指向函數(shù)的指針變量調(diào)用函數(shù)的優(yōu)越性。在后面將會(huì)看到,利用指針變量值的可變性,可以實(shí)現(xiàn)一類函數(shù)的調(diào)用。

8.6.3用指向函數(shù)的指針作函數(shù)的參數(shù)

例8.18實(shí)現(xiàn)兩個(gè)整數(shù)的加、減、乘、除運(yùn)算,用戶通過輸入1、2、3、4分別進(jìn)行加法、減法、乘法、除法運(yùn)算并輸出結(jié)果。

編程思路:分別設(shè)計(jì)實(shí)現(xiàn)兩整數(shù)加、減、乘、除的函數(shù),再設(shè)計(jì)一個(gè)參數(shù)為指針的函數(shù),在主函數(shù)中根據(jù)用戶的選擇,傳遞相應(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)算類函數(shù)。

程序運(yùn)行時(shí),在主函數(shù)中接收用戶的運(yùn)算選擇,switch語句依據(jù)選擇調(diào)用arith函數(shù),把兩運(yùn)算數(shù)和相應(yīng)功能運(yùn)算函數(shù)指針傳遞給形參,由“result=(*p)(x,y);”語句調(diào)用相應(yīng)功能運(yùn)算函數(shù)。顯然,p是指向函數(shù)的指針變量,可以接收不同函數(shù)的指針(函數(shù)入口地址),通過它來實(shí)現(xiàn)多個(gè)函數(shù)的調(diào)用。參數(shù)傳遞及p的指向如圖8.12所示。

圖8.12例8.17參數(shù)傳遞及指針變量的指向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ù)中,通過返回指針就可以引用操作后的數(shù)組元素。

返回指針值的函數(shù),要將返回值類型定義為指針型。其定義的一般形式為

基類型符*函數(shù)名(參數(shù)表列);顯然,返回指針值函數(shù)的定義和返回基本類型值函數(shù)定義的區(qū)別僅在于基類型符和函數(shù)名之間加了一個(gè)“*”。這樣,就定義了該函數(shù)是返回指向一種基類型數(shù)據(jù)的指針。從C語言運(yùn)算符優(yōu)先級(jí)來講,因()優(yōu)先級(jí)高于*,所以數(shù)組名先與()結(jié)合,顯然這是函數(shù)形式。函數(shù)前面有一個(gè)*,表示此函數(shù)是指針型函數(shù)(函數(shù)值是指針)。最前面的基類型是返回的指針?biāo)赶驍?shù)據(jù)的類型。

下面通過例子來說明使用返回指針值函數(shù)的程序設(shè)計(jì)方法。

例8.19

編程實(shí)現(xiàn)一個(gè)班學(xué)生課程成績的查詢。要求輸入一個(gè)學(xué)生的序號(hào)后,能輸出該學(xué)生全部課程的成績。用指針型函數(shù)來實(shí)現(xiàn)。

編程思路:一個(gè)班學(xué)生成績表用二維數(shù)組來表示。定義一個(gè)查詢函數(shù),根據(jù)學(xué)生序號(hào),確定一個(gè)指向該學(xué)生各門課成績行的指針,此指針作為函數(shù)返回值。在主函數(shù)中,通過返回指針可輸出該學(xué)生的各門課成績。為簡化數(shù)據(jù)而突出程序的設(shè)計(jì)方法,假設(shè)一個(gè)班有3個(gè)學(xué)生、4門課成績。

分析:函數(shù)search定義為指針型函數(shù),形參pointer是指向包含4個(gè)實(shí)型數(shù)據(jù)元素的一維數(shù)組的指針變量。函數(shù)中,定義pt是指向變量的指針變量。*(pointer+n)是引用第2層邏輯一維數(shù)組的第n個(gè)元素,即得到第n行首元素地址,把此地址賦給變量pt,作為函數(shù)返回值。主函數(shù)中,調(diào)用查詢函數(shù),將返回指針賦給p,在for循環(huán)中,通過p輸出所指向?qū)W生的各門課成績。請(qǐng)注意程序中所定義的指針變量p、pt和pointer的區(qū)別。

例8.20

在一個(gè)班學(xué)生成績表中,查找不及格課程的學(xué)生,輸出序號(hào)及成績。

編程思路:在例8.19的基礎(chǔ)上,定義查詢函數(shù)為查找一個(gè)學(xué)生有無不及格成績。如有則確定一個(gè)指向該學(xué)生成績行首的指針;如無,則設(shè)置指針為空。指針作為函數(shù)返回值。在主函數(shù)中,要按學(xué)生序號(hào)逐一調(diào)用查詢函數(shù),根據(jù)返回指針值判定是否有不及格成績。若有,則通過返回指針輸出該學(xué)生各門課成績。若無則不輸出。分析:定義函數(shù)search檢查一個(gè)學(xué)生有無不及格成績。形參pointer是指向包含4個(gè)實(shí)型數(shù)據(jù)元素的一維數(shù)組的指針變量。函數(shù)中,定義pt是指向?qū)嵭妥兞康闹羔樧兞?。循環(huán)實(shí)現(xiàn)檢查一個(gè)學(xué)生每門課成績是否低于60。若是,則將該學(xué)生成績行首地址賦給pt;若無,則pt為NULL。函數(shù)調(diào)用后,返回pt值。在主函數(shù)中將pt值賦給p,if語句判定p的值是否等于一個(gè)學(xué)生成績行首地址*(score+i)。若是,則該學(xué)生有不及格成績,用for循環(huán)輸出該學(xué)生各門課成績。若不是,則該學(xué)生無不及格成績,不輸出。

8.7多重指針與指針數(shù)組

何謂多重指針?怎樣通過多重指針引用數(shù)據(jù)?

在本章開頭已經(jīng)介紹,通過指針變量來引用另一變量值的方式是間接訪問。這屬于“單級(jí)間址”。如果再有一個(gè)指針變量,使其指向目標(biāo)變量的指針變量,就形成了“二級(jí)間址”的方法,即形成了二重指針。從理論上講,間址方法可以延伸到更多的層級(jí),即多重指針。多重指針及其指向關(guān)系如圖8.13所示。在實(shí)際應(yīng)用中,很少使用超過二級(jí)的間址。間址層級(jí)越多,越難理解,出錯(cuò)機(jī)會(huì)也會(huì)增多。

圖8.13多重指針示意圖8.7.1指針數(shù)組

若一個(gè)數(shù)組的元素均為指針類型數(shù)據(jù),則稱為指針數(shù)組。也就是說,指針數(shù)組中每一個(gè)元素都存放一個(gè)地址,相當(dāng)于一個(gè)指針變量。

使用指針數(shù)組也必須先定義。指針數(shù)組定義的一般形式為

基類型符*數(shù)組名[數(shù)組長度];

容易看出,指針數(shù)組與基本數(shù)據(jù)類型數(shù)組定義的區(qū)別在于:基類型符與數(shù)組名之間加了一個(gè)“*”,表示數(shù)組元素是指針類型,這些指針都指向基類型數(shù)據(jù)。從C語言運(yùn)算符優(yōu)先級(jí)看,由于[]比*優(yōu)先級(jí)高,因此數(shù)組名先與[數(shù)組長度]結(jié)合,這顯然是數(shù)組形式,再與*結(jié)合,表示此數(shù)組是指針類型。

例如:

int*a[4];

定義了數(shù)組a,包含4個(gè)指針類型元素,這些指針元素都指向整型數(shù)據(jù)。請(qǐng)注意指針數(shù)組定義與指向一維數(shù)組的指針變量定義的區(qū)別。如果寫成“int(*a)[4];”,就成為指向一維數(shù)組的指針變量的定義了。

指針數(shù)組最典型的應(yīng)用是:指向若干個(gè)字符串,使字符串處理更加方便靈活。例如,圖書館有若干本書,把書名放在一起組成書目,經(jīng)常需要對(duì)書目進(jìn)行排序和查詢。因字符串本身就是一個(gè)字符數(shù)組,存放書目的多個(gè)字符串需要設(shè)計(jì)一個(gè)二維字符數(shù)組。但在定義二維數(shù)組時(shí),需要確定列數(shù),一旦列數(shù)設(shè)定,二維數(shù)組中每一行包含的元素個(gè)數(shù)就是相等的。實(shí)際上,書名字符串的長度一般是不相等的。如果按最長的字符串來定義列數(shù),則會(huì)浪費(fèi)許多存儲(chǔ)單元,而且在進(jìn)行字符串處理時(shí)也會(huì)降低效率。使用指針數(shù)組,可以分別定義一些字符串,用指針數(shù)組的元素分別指向各字符串,如圖8.14所示。這樣能有效解決上述問題。

圖8.14多個(gè)字符串的存儲(chǔ)及與指針數(shù)組的指向示意圖

例8.21將書目字符串按字母順序輸出。

編程思路:定義一個(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語句的作用是比較兩個(gè)字符串,并將小串的序號(hào)記錄在變量k中。當(dāng)執(zhí)行完內(nèi)循環(huán)for語句后,第k串最小。若k≠i,就表示最小串不是第i串。故將name[i]和name[k]對(duì)換,也就是將其指向互換。執(zhí)行完sort函數(shù)后,指針數(shù)組元素的指向如圖8.15所示。圖8.15排序后指針數(shù)組元素的指向

printf的作用是輸出排好序的字符串。因name[0]~name[4]分別指向從小到大排好序的字符串,所以按指針數(shù)組元素順序,用“%s”格式控制,就能輸出排好序的字符串。

8.7.2指向指針數(shù)據(jù)的指針

指針數(shù)組的存儲(chǔ)同基本類型數(shù)組一樣,按元素順序連續(xù)存儲(chǔ)在一個(gè)存儲(chǔ)區(qū),每個(gè)元素都有一個(gè)存儲(chǔ)地址。所以,可定義一個(gè)指向指針數(shù)組元素的指針變量,通過該指針變量可以引用指針數(shù)組中的元素。這種指針變量稱為指向指針數(shù)據(jù)的指針變量。指向指針數(shù)據(jù)的指針變量定義的一般形式為

基類型符**變量名;

從C語言運(yùn)算符的優(yōu)先級(jí)來看,*運(yùn)算符的結(jié)合性是從右到左,因此,“**變量名”相當(dāng)于*(*變量名),顯然“*變量名”是指針變量的定義形式,前面再加一個(gè)“*”,表示該指針變量指向指針型數(shù)據(jù)。

在前面定義書目指針數(shù)組的基礎(chǔ)上,看下面語句的功能作用:

char**p;

p=name+2;

printf("%d\n",*p);

printf("%s\n",*p);

其中,第1個(gè)printf函數(shù)語句中,“*p”是引用字符型指針數(shù)組元素name[2]的值,輸出的是一個(gè)地址。第2個(gè)printf函數(shù)語句中,“*p”與“%s”配合,輸出的是name[2]所指向的字符串。

例8.22使用指向指針數(shù)據(jù)的指針變量輸出多個(gè)字符串。

編程思路:定義一個(gè)指針數(shù)組,并對(duì)其初始化,使數(shù)組中的每一個(gè)元素分別指向5個(gè)字符串。定義一個(gè)指向指針型數(shù)據(jù)的指針變量,使其先后指向指針數(shù)組各元素,輸出這些元素所指向的字符串。

運(yùn)行結(jié)果:

分析:在主函數(shù)的開頭,定義了指向字符的指針數(shù)組name,同時(shí)用字符串賦初值的方式初始化,分別將5個(gè)字符串的首地址賦給數(shù)組元素。又定義指向指針數(shù)據(jù)的指針變量p,在for循環(huán)中,賦值語句“p=name+i;”使p指向指針數(shù)組的第i元素,即第i個(gè)字符串的首地址。在“printf("%s\n",*p);”語句中使用“%s”,輸出p所指向的字符串。上面針對(duì)指針數(shù)組元素指向字符串的典型應(yīng)用舉了兩個(gè)例子。指針數(shù)組元素也可指向基本類型數(shù)據(jù)。下面通過一個(gè)簡單例子來說明這種用法。

例8.23使用指針數(shù)組元素指向一個(gè)整型數(shù)組的元素,用指向指針數(shù)據(jù)的指針變量輸出整型數(shù)組各元素的值。

運(yùn)行結(jié)果:

分析:主函數(shù)中定義了一個(gè)整型數(shù)組a,又定義了一個(gè)指向整型數(shù)據(jù)的指針數(shù)組,同時(shí)對(duì)其初始化,將5個(gè)整型數(shù)組元素的地址賦給指針數(shù)組元素。在“printf("%d",**p);”語句中,“**p”屬于二重間址訪問,從p所指向的指針數(shù)組得到數(shù)據(jù)元素的地址,再由元素地址從數(shù)據(jù)數(shù)組中取得元素值輸出。

8.8用于動(dòng)態(tài)內(nèi)存分配的指針型函數(shù)

8.8.1內(nèi)存動(dòng)態(tài)分配的函數(shù)

1.開辟動(dòng)態(tài)存儲(chǔ)空間函數(shù)(malloc)

該函數(shù)原型為

void*malloc(unsignedintsize);

其作用是在內(nèi)存的動(dòng)態(tài)存儲(chǔ)區(qū)中分配一個(gè)長度為size字節(jié)的連續(xù)空間。此函數(shù)是指針型函數(shù),返回值是所分配存儲(chǔ)區(qū)域的起始地址,如果分配未成功,則返回空指針(NULL)。利用返回指針可以使用該存儲(chǔ)區(qū)域中的數(shù)據(jù)。例如:

malloc(100);

開辟100字節(jié)的動(dòng)態(tài)存儲(chǔ)區(qū),函數(shù)返回該區(qū)域首字節(jié)地址。

2.開辟數(shù)組動(dòng)態(tài)存儲(chǔ)空間函數(shù)(calloc)

該函數(shù)原型為

void*calloc(unsignedn,unsignedsize);

其作用是在內(nèi)存的動(dòng)態(tài)存儲(chǔ)區(qū)中分配n個(gè)長度為size的連續(xù)空間,即可為n個(gè)元素的數(shù)組開辟一個(gè)動(dòng)態(tài)存儲(chǔ)區(qū),每個(gè)元素的存儲(chǔ)長度為size。函數(shù)的返回值是所分配存儲(chǔ)區(qū)域的起始地址,如果分配未成功,則返回空指針(NULL)。利用返回指針可以使用該存儲(chǔ)區(qū)域中的數(shù)據(jù)元素。例如:

p=calloc(50,4);

開辟

溫馨提示

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

最新文檔

評(píng)論

0/150

提交評(píng)論