《計算思維與C語言概述》-第六章_第1頁
《計算思維與C語言概述》-第六章_第2頁
《計算思維與C語言概述》-第六章_第3頁
《計算思維與C語言概述》-第六章_第4頁
《計算思維與C語言概述》-第六章_第5頁
已閱讀5頁,還剩101頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

6.1指針的基本概念

6.1.1

指針與地址地址是指計算機內(nèi)存的地址。例如,某臺計算機有64KB內(nèi)存,

實際上是說這臺計算機具有64KB的內(nèi)存空間,即有64×1024=216字節(jié)的內(nèi)存空間。1字節(jié)是一個基本內(nèi)存單元,可以存放8位二進制數(shù)??梢园衙孔止?jié)想象成一個小抽屜,

為了區(qū)分每一個小抽屜,將它們用二進制數(shù)編號,

通常從0開始遞增,

每字節(jié)對應(yīng)唯一的編號。如果用二進制數(shù)表示,則64KB的內(nèi)存編號為16位;如果用十六進制數(shù)表示,則64KB的內(nèi)存編號為4位,其取值范圍為0000~FFFF。編號實際上就是地址,通過某一個地址,

就可以唯一地定位到某一字節(jié)。程序中使用的常量、定義的變量等,在運行時要占據(jù)一定數(shù)目的連續(xù)的內(nèi)存單元,計算機就通過這些內(nèi)存單元的地址尋找并使用它們。例如,變量聲明“inta=5”對應(yīng)的存儲空間如圖6-1所示。下一頁返回6.1指針的基本概念

從中可以看到,變量a本身占用一定數(shù)目的內(nèi)存單元,數(shù)據(jù)類型int表明變量a占用的內(nèi)存單元的字節(jié)數(shù),不同的編譯系統(tǒng)有不同的大小,在此設(shè)為4字節(jié)。假定內(nèi)存空間為64KB,則內(nèi)存地址使用4位十六進制數(shù)編號,設(shè)4個內(nèi)存單元的地址為1000、1001、1002、1003。指針是存放內(nèi)存地址值的一種數(shù)據(jù)類型。指針定義的內(nèi)存單元存放的是另一個內(nèi)存單元的地址。通常,將指針指代內(nèi)存單元的地址,將存儲了地址的變量稱為指針變量。上一頁下一頁返回6.1指針的基本概念

6.1.2

指針變量的定義C語言中,指針變量定義的一般形式:類型名?指針變量名;指針變量的定義和普通變量的定義基本相同,但在指針變量前必須增加一個星號?,表示這個變量里面存放的是地址。例如:int?p;float?q;該語句分別定義了指針變量p和q,分別存儲整型變量和浮點型變量的地址。方便起見,有時候也稱變量p的類型是int?,

稱變量q的類型是float?。上一頁下一頁返回6.1指針的基本概念

6.1.3

指針變量的初始化與賦值定義一個指針變量后,編譯器會為指針變量分配存儲空間,但不存入任何地址值,即沒有使指針變量指向一個變量。

未初始化的指針變量值是隨機的,

如果指向內(nèi)存中比較重要的位置,那么指針操作可能導(dǎo)致系統(tǒng)異常。因此,在使用指針變量前,

對其進行初始化或賦值是至關(guān)重要的。在定義指針變量的同時,可以對其進行初始化。例如:inta=5;int?pa=&a;其中,一元運算符“&”得到變量的地址,將變量a的地址作為指針變量pa的初始值,變量pa存儲圖6-1所示中變量a的地址,對應(yīng)的存儲空間如圖6-2所示。指針變量pa由于是變量,所以要占用一定數(shù)目的內(nèi)存單元。上一頁下一頁返回6.1指針的基本概念

假定內(nèi)存空間為64KB,設(shè)指針變量pa所占內(nèi)存單元的首地址為2000。存儲的內(nèi)容是變量a所對應(yīng)的存儲空間地址1000,需要占用2個內(nèi)存單元。對此,可以形象地稱指針變量pa指向變量a,或稱pa是指向變量a的指針,如圖6-2中箭頭所示。聲明一個指針變量后,就可以將已經(jīng)定義的變量的地址賦值給它。例如:int?pa,a;pa=&a;也可以將一個指針變量的值賦給另一個指針變量。例如:int?pa,a=3,?qa;pa=&a;qa=pa;上一頁下一頁返回6.1指針的基本概念

在聲明指針變量時,如果沒有確切的地址可以賦值,那么為指針變量賦一個空指針是一種良好的編程風(fēng)格。賦為NULL值的指針稱為空指針。NULL是一個定義在標(biāo)準(zhǔn)庫中的值為零的常量。例如:int?ptr=NULL;該語句表示ptr為空指針,其值為0。注意:(1)&運算符只能用于變量

(包括指針變量),但不能用于表達式、

常量等。(2)不要引用沒有被賦值的指針。(3)不同類型的指針之間不能賦值。上一頁下一頁返回6.1指針的基本概念

6.1.4

指針變量的訪問當(dāng)建立了指針變量和變量之間的指向關(guān)系后,就可以通過指針變量訪問變量了。C語言定義了一個取指針指向的值的運算符?。一元運算符?是間接尋址或間接引用運算符。當(dāng)它作用于指針時,將訪問指針?biāo)赶虻淖兞?,例如,inta=5,b;in?pa=&a;b=?p;最后一條語句相當(dāng)于:b=a;上一頁下一頁返回6.1指針的基本概念

可以看出,

當(dāng)指針變量pa指向變量a后,

通過變量名可以訪問變量a,

通過?pa也可以訪問變量a,

這是兩種等價的訪問變量的方法。通過變量名a訪問變量是直接訪問,編譯器會將變量名a映射為變量a的地址,通過地址直接定位到變量a的存儲空間。借助指針變量pa來訪問變量a是間接訪問。這時,首先訪問指針變量pa,獲取指針變量的值(即變量a的地址),然后通過變量a的地址訪問變量。上一頁返回6.2指針與數(shù)組

本節(jié)以一個簡單示例為基點,闡述使用指針的必要性及使用指針來動態(tài)分配內(nèi)存的靈活性。【例6-1】從鍵盤輸入一組整型數(shù)據(jù)存入數(shù)組。【分析】將一組整型數(shù)據(jù)存入數(shù)組,必須知道數(shù)組的長度。在本例中,首先輸入數(shù)據(jù)的個數(shù)n(5≤n≤100),

然后輸入n個數(shù)據(jù)。程序的代碼如下:下一頁返回6.2指針與數(shù)組

上一頁下一頁返回6.2指針與數(shù)組

其中,

聲明“intdata[100]”

在編譯階段向操作系統(tǒng)申請了100個整型數(shù)據(jù)的空間。

然而,在每次程序運行中,僅有個別情況使用接近100個數(shù)據(jù),而大多數(shù)情況下只使用了少量數(shù)據(jù),內(nèi)存空間浪費比較嚴重,而使用指針進行動態(tài)分配內(nèi)存就能輕松解決此問題。上一頁下一頁返回6.2指針與數(shù)組

6.2.1

用指針操作數(shù)組C語言中規(guī)定,數(shù)組的名字代表數(shù)組首元素的地址。例如,定義了這樣的整型數(shù)組:inta[]={2,4,6,7,8};假定內(nèi)存空間為64KB,地址為4位十六進制數(shù),整型數(shù)據(jù)所占的內(nèi)存單元個數(shù)為4,它在內(nèi)存中的存儲示意如圖6-3所示。

數(shù)組名a的值為元素a[0]的地址,即1000;a實際上是一個int?型的指針,始終指向元素a[0],但是這個值只能作為常量來看待,不能被修改。指針是一個用數(shù)值表示的地址,因此可以對指針執(zhí)行算術(shù)運算。可以對指針進行的算術(shù)運算有四種:++、--、+、-。圖6-3所示中的數(shù)組名a可以看作一個指針常量,可以進行+、-運算,而指針變量可以進行++、--、+、-運算。上一頁下一頁返回6.2指針與數(shù)組

例如,“a+1”為下一個整數(shù)位置,其值為1000+4=1004。考慮下列代碼:int?ptr=a;ptr++;printf("%d",?ptr);在執(zhí)行完上述運算后,ptr?qū)⒅赶蛭恢茫保埃埃?,因為ptr每增加一次?/p>

它都將指向下一個整數(shù)位置,

即當(dāng)前位置往后移4字節(jié)。

這個運算會在不影響內(nèi)存位置中實際值的情況下,

移動指針到下一個內(nèi)存位置。如果ptr指向一個地址為1000的字符,

上面的運算會導(dǎo)致指針指向位置1001,因為下一個字符位置是在1001。上一頁下一頁返回6.2指針與數(shù)組

既然數(shù)組名為數(shù)組內(nèi)存區(qū)域的首地址,那么利用指針就可以方便地操作數(shù)組。例6-1利用指針操作數(shù)組元素的程序代碼如下:上一頁下一頁返回6.2指針與數(shù)組

上一頁下一頁返回6.2指針與數(shù)組

6.2.2

動態(tài)內(nèi)存分配如果預(yù)先知道數(shù)組的大小,那么在定義數(shù)組時就比較容易。例如,有一個存儲人名的數(shù)組,它最多容納100個字符,就可以如下定義數(shù)組:charname[100];但是,在有些情況下,預(yù)先不知道需要存儲的文本長度。例如,

存儲有關(guān)一個主題的詳細描述,而且并不確定主題的具體長度。對此,可以定義一個指針,將該指針指向未確定大小的內(nèi)存大小的字符,后續(xù)根據(jù)需求來分配內(nèi)存。上一頁下一頁返回6.2指針與數(shù)組

C語言為內(nèi)存的分配和管理提供了幾個函數(shù)。這些函數(shù)可以在stdlibh頭文件中找到。比較常用的有:(1)內(nèi)存申請———void?malloc(intnum):在堆區(qū)分配一塊指定大小的內(nèi)存空間,

用來存放數(shù)據(jù)。這塊內(nèi)存空間在函數(shù)執(zhí)行完成后不會被初始化,它們的值是未知的。(2)內(nèi)存申請———void?calloc(intnum,intsize):在內(nèi)存中動態(tài)地分配num個長度為size的連續(xù)空間,并將每字節(jié)都初始化為0。所以它的結(jié)果是分配了num×size字節(jié)長度的內(nèi)存空間,且每字節(jié)的值都是0。(3)內(nèi)存釋放———voidfree(void?address):該函數(shù)釋放address所指向的內(nèi)存塊,

釋放的是動態(tài)分配的內(nèi)存空間。上一頁下一頁返回6.2指針與數(shù)組

對于例6-1,利用指針動態(tài)分配內(nèi)存來編寫的程序代碼如下:上一頁下一頁返回6.2指針與數(shù)組

上一頁下一頁返回6.2指針與數(shù)組

【練習(xí)】編寫一個程序,輸入若干整數(shù),利用動態(tài)分配來申請相應(yīng)大小的內(nèi)存空間,計算并輸出其均值與標(biāo)準(zhǔn)偏差。提示:設(shè)xi指數(shù)據(jù)元素,n表示數(shù)據(jù)元素的個數(shù)。上一頁下一頁返回6.2指針與數(shù)組

6.2.3

數(shù)組作為函數(shù)參數(shù)由6.2.1可知,數(shù)組名是該數(shù)組的首地址。本質(zhì)上,數(shù)組名就是指針常量。因此,當(dāng)實參是數(shù)組時,只需要傳遞數(shù)組的首地址,并不需要復(fù)制整個數(shù)組元素。設(shè)有一個一維整型數(shù)組arr和無返回值的函數(shù)fun,當(dāng)數(shù)組作為形參時,函數(shù)原型可以有以下幾種寫法:voidfun(intarr[6]);voidfun(intarr[]);voidfun(int?);以上不同的函數(shù)聲明的作用是一樣的,都在傳送一個地址,也就是一個指針。上一頁下一頁返回6.2指針與數(shù)組

【例6-2】從鍵盤上順序輸入6個整型數(shù)據(jù),存放在一個一維整型數(shù)組,然后輸出。再將數(shù)組中的數(shù)據(jù)以相反的順序存入這個數(shù)組,然后輸出。(數(shù)組中的數(shù)據(jù)以相反的順序存入數(shù)組可用函數(shù)reverse實現(xiàn))【分析】數(shù)據(jù)輸入和輸出容易實現(xiàn),難點是將數(shù)組中的數(shù)據(jù)以相反的順序存入數(shù)組。數(shù)組的順序由數(shù)組元素的下標(biāo)確定,為了逆序存放數(shù)組元素,需要如下進行數(shù)據(jù)交換:上一頁下一頁返回6.2指針與數(shù)組

其中,任何一對數(shù)據(jù)交換都是兩個數(shù)的交換問題。上面三對數(shù)據(jù)的交換操作相同,所以可以用一個for循環(huán)完成:上一頁下一頁返回6.2指針與數(shù)組

把這個for循環(huán)作為函數(shù)reverse的函數(shù)體,就可以實現(xiàn)數(shù)組中的數(shù)據(jù)以相反的順序存放在數(shù)組的功能。由于只要求修改數(shù)組中數(shù)據(jù)元素的存放順序,并沒有要求返回什么信息,所以函數(shù)reverse的數(shù)據(jù)類型是void;由于需要修改數(shù)組本身存儲的數(shù)據(jù),也就是函數(shù)reverse對數(shù)組的影響在回到主函數(shù)時需要保留下來,所以形參中需要傳遞數(shù)組的地址,即一個指針。程序的代碼如下:上一頁下一頁返回6.2指針與數(shù)組

上一頁下一頁返回6.2指針與數(shù)組

上一頁下一頁返回6.2指針與數(shù)組

上一頁下一頁返回6.2指針與數(shù)組

運行結(jié)果:運行結(jié)果中可以發(fā)現(xiàn),在函數(shù)reverse中對數(shù)組操作的影響,其實就是對主函數(shù)中數(shù)組的操作。原因:數(shù)組作為參數(shù)時,函數(shù)傳遞數(shù)組的首地址,其實就是地址傳遞,所以實參的地址會傳遞給形參,形參變量(指針型變量)會根據(jù)地址找到實參的內(nèi)存單元,即通過形參間接操作了實參的數(shù)值。上一頁下一頁返回6.2指針與數(shù)組

【練習(xí)】(1)從鍵盤上順序輸入n個整型數(shù)據(jù),

存放在一個一維整型數(shù)組,

然后輸出。

接下來,將數(shù)組中的數(shù)據(jù)以相反的順序存入這個數(shù)組,然后輸出。(數(shù)組中的數(shù)據(jù)以相反的順序存入數(shù)組可編寫函數(shù)reverse實現(xiàn))提示:動態(tài)開辟一維數(shù)組;確定長度為n的一維數(shù)組有幾組數(shù)據(jù)需要交換,即如何控制循環(huán)次數(shù)。(2)從鍵盤上順序輸入n個整型數(shù)據(jù),

存放在一個一維整型數(shù)組,

然后輸出。

接下來,將數(shù)組中的數(shù)據(jù)按照從小到大的順序存入這個數(shù)組,然后輸出。(數(shù)組中的數(shù)據(jù)按照從小到大的順序存入數(shù)組可編寫函數(shù)sort實現(xiàn))提示:動態(tài)開辟一維數(shù)組;排序問題可以考慮冒泡排序。上一頁下一頁返回6.2指針與數(shù)組

(3)從鍵盤上順序輸入一個3×3的整型矩陣,

存放在一個二維整型數(shù)組,

然后輸出。接下來,對矩陣進行轉(zhuǎn)置操作。所謂轉(zhuǎn)置,就是進行矩陣元素的行號和列號的對換,也就是第i行第j列的數(shù)據(jù)和第j行第i列的數(shù)據(jù)交換位置。完成轉(zhuǎn)置操作后,輸出矩陣。(矩陣的轉(zhuǎn)置過程可編寫函數(shù)transposition實現(xiàn))(4)從鍵盤上順序輸入一個n×n的整型矩陣,

存放在一個二維整型數(shù)組,

然后輸出。最后,對矩陣進行轉(zhuǎn)置操作。提示:動態(tài)開辟二維數(shù)組。二維數(shù)組其實是一維數(shù)組的數(shù)組。上一頁返回6.3指針數(shù)組和指向指針的指針

6.3.1

指針數(shù)組在介紹指針數(shù)組的概念之前,先通過一個實例來對比使用二維數(shù)組和指針數(shù)組的區(qū)別?!纠叮场枯敵龆鄠€字符串?!痉治觥吭冢谜Z言中,一個字符串存儲在一個字符數(shù)組中。例如:chars1[]="hello";這時,數(shù)組元素的個數(shù)為6,包括字符的個數(shù)和字符串結(jié)束符'\0'。如果定義多個字符串,則可以聲明一個二維字符數(shù)組,但需要確定每一維元素的個數(shù)。例如:charstr[3][20];下一頁返回6.3指針數(shù)組和指向指針的指針

使用二維字符數(shù)組來實現(xiàn)例6-3的程序代碼如下:上一頁下一頁返回6.3指針數(shù)組和指向指針的指針

但是,這種方式容易造成存儲空間的浪費。例如,在上述str數(shù)組中,為了存儲最后一個較長的字符串,可將列數(shù)定義為20。但是前兩個字符串僅分別用了6字節(jié)和4字節(jié)(包括字符串的結(jié)束標(biāo)記'\0'),

字符串存儲方式如圖6-4所示。為了解決空間浪費的問題,可以通過使用指針來處理字符串。

如果需要同時處理多個字符串,就可以把這些指向字符串的指針放到一個數(shù)組中。使用指針數(shù)組來實現(xiàn)例6-3的程序代碼如下:上一頁下一頁返回6.3指針數(shù)組和指向指針的指針

上一頁下一頁返回6.3指針數(shù)組和指向指針的指針

在程序中,

str是一個包含3個元素的數(shù)組,

數(shù)組中的每個元素的類型都是char?,

即指向char型數(shù)據(jù)的指針,每個元素的值都是指向一個字符串的指針。與之前的程序相比,雖然多出了三個用來存放指針變量的存儲空間,但是字符串本身的空間沒有浪費,如圖6-5所示。上一頁下一頁返回6.3指針數(shù)組和指向指針的指針

6.3.2

指向指針的指針例6-3中的指針變量str是數(shù)組元素str[0]的地址,

而str[0]的值又是一個地址。

因此,str稱為指向指針的指針,但str是一個常量,不可變化。我們可以定義一個指向指針的指針變量,例如:char??p=str;p是指針變量,但需考慮這個指針變量的類型。定義p的目的是讓p指向指針數(shù)組str的一個元素,

而str的每個元素里面存放的是一個char?型的指針,

所以p是一個指向指針類型數(shù)據(jù)的指針,

簡稱指向指針的指針。

可將這種定義形式看作char?(?p),

從而將其理解為?p里面存儲的是一個char?型的指針數(shù)據(jù)。

所以,

p就是指向該指針數(shù)據(jù)的指針。上一頁下一頁返回6.3指針數(shù)組和指向指針的指針

使用指向指針的指針實現(xiàn)例6-3的程序代碼如下:上一頁下一頁返回6.3指針數(shù)組和指向指針的指針

【練習(xí)】口袋中有紅、黃、藍、白、黑5種顏色的球若干個。每次從口袋中任意取出3個球,編程實現(xiàn)得到3種不同顏色的球的可能取法,輸出每種排列的情況。提示:(1)使用指針數(shù)組表示5種顏色。(2)3次取球表示為3個變量i、j、k,分別表示指針數(shù)組的下標(biāo)。(3)使用暴力搜索法,搜索符合條件的排列。上一頁返回6.4指針作為函數(shù)的形參

指針變量也可以作為函數(shù)的形參,其作用是將一個變量的地址傳到一個函數(shù)中。這時,形參和實參實際上指向同一個內(nèi)存空間,主調(diào)函數(shù)和被調(diào)函數(shù)共享一塊內(nèi)存空間。此時,實參和形參都是地址,如指針變量或者指針常量(數(shù)組名)等。由515節(jié)可知,值傳遞不能實現(xiàn)兩個數(shù)的交換。原因就是形參中數(shù)值的變化對實參沒有影響,那么指針作為函數(shù)的形參能夠?qū)崿F(xiàn)兩個數(shù)的交換嗎?下一頁返回6.4指針作為函數(shù)的形參

【例6-4】使用指針作為函數(shù)的形參,設(shè)計兩個實數(shù)的交換函數(shù)swap?!痉治觥康冢嫡碌睦担仓薪榻B過兩個數(shù)的交換函數(shù)swap,在此把函數(shù)swap的形參類型修改為指針類型。程序的代碼如下:上一頁下一頁返回6.4指針作為函數(shù)的形參

上一頁下一頁返回6.4指針作為函數(shù)的形參

上一頁下一頁返回6.4指針作為函數(shù)的形參

運行結(jié)果:上一頁下一頁返回6.4指針作為函數(shù)的形參

以例6-4程序的運行結(jié)果可以看出,地址傳遞可以實現(xiàn)兩個數(shù)的交換。對比普通變量作參數(shù)的程序5-2c和程序6-3c,有以下兩個重要的區(qū)別:(1)swap函數(shù)的聲明不同,

定義也就不同。(2)調(diào)用swap函數(shù)時,

傳遞的實參不同。接下來,描述上述程序的執(zhí)行流程,并解釋變量a和變量b的值是如何在函數(shù)swap中被修改的。上一頁下一頁返回6.4指針作為函數(shù)的形參

假設(shè)變量a的地址是1000,變量b的地址是1008,變量x的地址是2000,變量y的地址是2008,中間變量z的地址是2040。程序的執(zhí)行流程如下:首先,

函數(shù)swap相應(yīng)的形參x和y被定義為double?型,

在main函數(shù)中調(diào)用swap(&a,&b)時,

實參分別是&a和&b。

所以,

實參&a的值傳遞給形參x,

實參&b的值傳遞給形參y,如圖6-6所示,x和y分別被賦值為1000和1008,即變量a和b的地址。然后,

執(zhí)行swap函數(shù)。

swap函數(shù)交換的是?x和?y的值,

而?x和?y就分別指向函數(shù)main中的變量a和b。所以,swap函數(shù)中交換的是a和b的值,如圖6-7所示。最后,程序返回main函數(shù),輸出變量a和變量b的值。這時候輸出的是變量a和變量b交換之后的值,如圖6-8所示。上一頁下一頁返回6.4指針作為函數(shù)的形參

由此可見,通過指針作為參數(shù),可以在被調(diào)用函數(shù)中修改主調(diào)函數(shù)中定義的變量的值。但是這種做法的參數(shù)傳遞實際上仍采用了值傳遞。例如,在例6-4的程序中,雖然實參&a和形參x的值都是1000,但是&a和x并不等同。swap函數(shù)只是利用形參x和y間接訪問了main函數(shù)中的變量a和b,但它并沒有改變形參x和y的值。而且,即使形參x和y的值被改變了,這種改變也不會引起實參值&a和&b的改變。上一頁返回6.5函數(shù)指針和指針函數(shù)

函數(shù)指針和指針函數(shù)是經(jīng)常被混淆的兩個概念。函數(shù)指針是一個指針變量,只不過是指向函數(shù)的指針變量,該指針變量指向函數(shù)。指針函數(shù)是一個函數(shù),但返回類型是某一類型的指針。下一頁返回6.5函數(shù)指針和指針函數(shù)

6.5.1

函數(shù)指針指向函數(shù)的指針簡稱函數(shù)指針,函數(shù)指針是函數(shù)入口的內(nèi)存地址。函數(shù)指針的本質(zhì)是一個指針變量,該指針指向一個函數(shù)。聲明函數(shù)指針的一般格式:數(shù)據(jù)類型(?函數(shù)名)([形參列表]);其中,數(shù)據(jù)類型是函數(shù)返回值的數(shù)據(jù)類型;函數(shù)名是用戶定義的指針變量名;形參列表不是必需的。例如:intFun(intx,inty); /?聲明一個函數(shù)?/int(?pFun)(intx,inty);/?聲明一個函數(shù)指針?/函數(shù)指針需要把一個函數(shù)的地址賦值給它,一般有以下兩種寫法:函數(shù)指針變量=函數(shù)名;函數(shù)指針變量=&函數(shù)名;上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

取地址運算符&不是必需的,因為一個函數(shù)標(biāo)識符就表示了它的地址,編譯器會為每個函數(shù)分配一個首地址,即該函數(shù)第一條指令的地址。一般情況下,可以用一個指針來保存這個地址,而這個指針就是函數(shù)指針,該指針可以看作它指向函數(shù)的別名,所以可以用該指針來調(diào)用這個函數(shù)。如果是函數(shù)調(diào)用,還必須包含一個圓括號括起來的參數(shù)表??梢圆捎靡韵聝煞N方式來通過函數(shù)指針調(diào)用函數(shù):(?函數(shù)指針變量)(參數(shù))函數(shù)指針變量(參數(shù))第二種格式看上去和函數(shù)調(diào)用無異。但是有些程序員傾向于使用第一種格式,因為它明確指出通過指針而非函數(shù)名來調(diào)用函數(shù)。上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

【例6-5】從鍵盤上輸入2個正整型數(shù)據(jù),計算這兩個整型數(shù)據(jù)的最大公約數(shù),然后輸出最大公約數(shù)?!痉治觥壳髢蓚€整數(shù)的最大公約數(shù),常用的方法有更相減損術(shù)和輾轉(zhuǎn)相除法,這里以輾轉(zhuǎn)相除法為例。

輾轉(zhuǎn)相除法,

又名歐幾里得算法(Euclideanalgorithm),

是求兩個正整數(shù)的最大公約數(shù)的算法。它是已知最古老的算法,可追溯至公元前300年前。它的具體做法是:用較小數(shù)除較大數(shù),然后用出現(xiàn)的余數(shù)(第一余數(shù))去除除數(shù),再用出現(xiàn)的余數(shù)(第二余數(shù))去除第一余數(shù),如此反復(fù),直到最后余數(shù)是0為止。如果是求兩個數(shù)的最大公約數(shù),那么最后的除數(shù)就是這兩個數(shù)的最大公約數(shù)。例如:a=25,b=15,a%b=10,b%10=5,10%5=0,最后一個為被除數(shù)余數(shù)的除數(shù)就是5,5就是所求最大公約數(shù)。上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

設(shè)這兩個整數(shù)分別是x和y。首先,考慮x和y的大小關(guān)系(假定x是較大數(shù),y是較小數(shù)),保證較大數(shù)是被除數(shù),較小數(shù)是除數(shù),求得余數(shù)temp=x%y,然后用得到的余數(shù)去除除數(shù),相當(dāng)于把得到的余數(shù)賦值給被除數(shù)。然后,不斷進行上述過程,直到余數(shù)為0,就終止程序。代碼的核心部分如下:上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

假設(shè)解決最大公約數(shù)的函數(shù)名是commonDivisor,將以上代碼作為一個函數(shù)的函數(shù)體即可。函數(shù)的形參需要數(shù)據(jù)x和y。由于求解最后的結(jié)果是整數(shù)x和y的最大公約數(shù),所以該函數(shù)是有返回值的,

且為整型。

因此,

函數(shù)聲明可以寫成“intcommonDivisor(intx,inty);”上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

如果規(guī)定函數(shù)調(diào)用采用指向該函數(shù)的指針實現(xiàn),則程序代碼如下:上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

程序運行的結(jié)果如下:在該程序中,

定義了函數(shù)指針變量pFun,

并令它指向函數(shù)commonDivisor,

以便在需要的時候調(diào)用該函數(shù)。上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

6.5.2

函數(shù)指針作為函數(shù)參數(shù)———回調(diào)函數(shù)有了函數(shù)指針,就可以通過函數(shù)指針來調(diào)用函數(shù)。對指針的應(yīng)用是C語言編程的精髓所在,而回調(diào)函數(shù)就是C語言里面對函數(shù)指針的高級應(yīng)用。如果把函數(shù)指針(函數(shù)的入口地址)傳遞給另一個函數(shù),那么當(dāng)這個函數(shù)指針被用來調(diào)用它所指向的函數(shù)時,就稱這個函數(shù)是回調(diào)函數(shù)。簡而言之,回調(diào)函數(shù)是一個通過函數(shù)指針調(diào)用的函數(shù)。回調(diào)函數(shù)不是由該函數(shù)的實現(xiàn)方直接調(diào)用,而是在特定的事件或條件發(fā)生時由另一方調(diào)用,

用于對該事件或條件進行響應(yīng)。編程可以分為系統(tǒng)編程和應(yīng)用編程。所謂系統(tǒng)編程,可以將其理解成編寫庫,如C標(biāo)準(zhǔn)庫。C標(biāo)準(zhǔn)庫由在15個頭文件中聲明的函數(shù)、類型定義和宏組成,每個頭文件都代表一定范圍的編程功能。應(yīng)用編程就是利用寫好的各種庫來編寫具有某種功能的程序,也就是應(yīng)用。在求解問題時,可以調(diào)用C標(biāo)準(zhǔn)庫中的相關(guān)函數(shù)來實現(xiàn)某些功能。例如,應(yīng)用數(shù)學(xué)庫mathh中的函數(shù)sqrt實現(xiàn)開平方,進而求得三角形面積。上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

在實際編程過程中,經(jīng)常調(diào)用庫函數(shù)。既然根據(jù)函數(shù)指針可以找到某個函數(shù),那么在需要調(diào)用具有某個功能的函數(shù)時,就可以把指向該函數(shù)的函數(shù)指針作為調(diào)用函數(shù)的參數(shù),

從而實現(xiàn)整個函數(shù)在函數(shù)之間的傳遞?!纠叮丁繌逆I盤上輸入兩個字符串,設(shè)計一個比較這兩個字符串是否相等的函數(shù)?!痉治觥繛榱苏f明函數(shù)回調(diào)函數(shù),設(shè)計一個檢查兩個字符串是否相等的函數(shù)check,該函數(shù)的形參由兩個字符串以及函數(shù)指針變量cmp組成,且cmp指字符串系統(tǒng)庫函數(shù)strcmp。上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

字符串比較函數(shù)strcmp的一般調(diào)用形式是“strcmp(s1,s2)”。

其中,

s1和s2既可以是字符型數(shù)組,也可以是字符串常量。函數(shù)strcmp的功能是比較兩個字符串的大小:①如果s1<s2,則函數(shù)值為小于0的整數(shù)。②如果s1=s2,則函數(shù)值為0。③如果s1>s2,則函數(shù)值為大于0的整數(shù)。上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

定義函數(shù)check的具體代碼如下:在需要比較兩個字符串大小的位置,調(diào)用函數(shù)check即可。上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

整個程序的代碼如下:上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

運行結(jié)果:回調(diào)機制為程序提供了很強靈活性。在回調(diào)中,利用某種方式,就可以把回調(diào)函數(shù)像參數(shù)一樣傳入中間函數(shù)。上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

6.5.3

指針函數(shù)簡而言之,指針函數(shù)就是一個返回指針的函數(shù),其本質(zhì)是一個函數(shù),而該函數(shù)的返回值是一個指針。聲明指針函數(shù)的一般格式:數(shù)據(jù)類型?函數(shù)名(形參列表);其中,“數(shù)據(jù)類型?”是函數(shù)返回值的數(shù)據(jù)類型,它是一個指針。對比下面的兩條函數(shù)聲明:intFun(intx,inty); /?聲明一個函數(shù)?/int?Fun(intx,inty); /?聲明一個指針函數(shù)?/第1條函數(shù)聲明容易理解,其實就是聲明一個函數(shù),返回值是int類型,是一個數(shù)值。第2條函數(shù)聲明和第1條函數(shù)聲明的唯一區(qū)別就是在函數(shù)名前面多了一個?號,因此這是一個指針函數(shù),其實它也是一個函數(shù),其返回值是一個int?型的指針,是一個地址。與普通函數(shù)對比,指針函數(shù)返回了一個指針(即地址值)而已。上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

指針函數(shù)返回值需要用同類型的指針變量來接收。也就是說,在主調(diào)函數(shù)中,函數(shù)返回值必須賦給同類型的指針變量。注意:在返回指針時,

必須保證指針指向的對象在函數(shù)執(zhí)行結(jié)束后是存在的。上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

【例6-7】從鍵盤上輸入n個整型數(shù)據(jù),存放在數(shù)組中并壓縮求和。壓縮規(guī)則:奇數(shù)位置的數(shù)據(jù)和一個相鄰偶數(shù)位置的數(shù)據(jù)相加,求和結(jié)果依次存放在另一個數(shù)組中,例如,1號位置的數(shù)據(jù)加上2號位置的數(shù)據(jù),結(jié)果存放在另一個數(shù)組的1號位置;3號位置的數(shù)據(jù)和4號位置的數(shù)據(jù)相加,結(jié)果存放在另一個數(shù)組的2號位置;依次類推。要求:(1)數(shù)組的0號位置不使用。(2)如果數(shù)組中最后僅剩下一個數(shù)據(jù)元素,

則該元素直接存放在另一個數(shù)組中,

相當(dāng)于最后一個位置的數(shù)據(jù)加了元素零。(3)將結(jié)果數(shù)組返給主函數(shù),

然后輸出。上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

【分析】這里需要兩個數(shù)組、一個數(shù)組存儲數(shù)據(jù),稱為數(shù)據(jù)數(shù)組;另一個數(shù)組存儲壓縮求和的結(jié)果,稱為結(jié)果數(shù)組。由于數(shù)據(jù)元素的個數(shù)不確定,所以需要動態(tài)開辟一維數(shù)組;

又因題目中要求數(shù)組的0號位置不使用,所以n個數(shù)據(jù)元素需要的存儲空間是n+1個,即數(shù)據(jù)數(shù)組需要n+1個存儲單元。由于是兩個數(shù)據(jù)求和的結(jié)果存放在結(jié)果數(shù)組中,因此結(jié)果數(shù)組的長度是(n+1)/2+1。按照壓縮求和的原則,當(dāng)數(shù)據(jù)的長度是偶數(shù)時,剛好可以兩兩相加;當(dāng)數(shù)據(jù)的長度是奇數(shù)時,

最后一個數(shù)據(jù)元素會剩下。

相加的規(guī)律可以描述成a[i]+a[i+1],

循環(huán)條件就是i≤n。但是,

當(dāng)i=n時,

a[i]+a[i+1]中的i+1會出現(xiàn)數(shù)組越界。

所以,

改用a[i-1]+a[i],將i的初始值設(shè)為2,這樣就可以避免出現(xiàn)數(shù)組越界的情況。最后,單獨處理數(shù)據(jù)長度是奇數(shù)的情況。如果數(shù)據(jù)的長度是奇數(shù),則將數(shù)據(jù)數(shù)組的最后一個數(shù)據(jù)元素直接賦值給結(jié)果數(shù)組中的最后一個位置。上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

程序的代碼如下:上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

運行結(jié)果:函數(shù)sunArray的返回值是個整型指針,所以在主函數(shù)中調(diào)用函數(shù)sunArray時,其返回值也用相同類型的指針變量來接收,該程序接收返回值的變量是result,且result是整型指針。上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

【練習(xí)】(1)從鍵盤上輸入兩個整型數(shù)據(jù),

用更相減損術(shù)來計算這兩個整型數(shù)據(jù)的最大公約數(shù),然后輸出最大公約數(shù)。要求設(shè)計一個函數(shù)來實現(xiàn)求兩個數(shù)的最大公約數(shù),

并在主函數(shù)中調(diào)用該函數(shù)。(2)從鍵盤上輸入兩個整型數(shù)據(jù),

計算這兩個整型數(shù)據(jù)的最小公倍數(shù),

然后輸出最小公倍數(shù)。要求設(shè)計一個函數(shù)來實現(xiàn)求兩個數(shù)的最小公倍數(shù),并在主函數(shù)中調(diào)用該函數(shù)。(3)從鍵盤上任意輸入一個整數(shù),

輸出該整數(shù)各位數(shù)字之和。

要求設(shè)計一個函數(shù)來實現(xiàn)求一個數(shù)的各位數(shù)字之和,并在主函數(shù)中調(diào)用該函數(shù)。上一頁下一頁返回6.5函數(shù)指針和指針函數(shù)

(4)不斷地從鍵盤上輸入一個整型數(shù)據(jù),

并判斷該整型數(shù)據(jù)是否為素數(shù),

直到EOF終止程序。要求設(shè)計函數(shù)isPrime,該函數(shù)用來判斷某整型數(shù)據(jù)是否為素數(shù),若是素數(shù),函數(shù)返回1,否則返回0。最后,在主函數(shù)中調(diào)用該函數(shù),根據(jù)返回結(jié)果輸出信息:如果函數(shù)返回值是1,就輸出“Itisaprimenumber”;否則輸出“Itisnotaprimenumber”。提示:EOF結(jié)束的使用語句:while(scanf("%f",&a)!=EOF){ }如果輸入數(shù)據(jù)有多組,則每組占一行。每行有兩個整數(shù)(a和n),分別用空格分隔。讀到文件結(jié)束的輸入形式如下:while(scanf("%d%d",&a,&n)!=EOF){ }上一頁返回6.6實訓(xùn)與實訓(xùn)指導(dǎo)

實訓(xùn)1

將整型數(shù)轉(zhuǎn)換為字符串設(shè)計一個函數(shù)char?itos(intn),

將整型數(shù)n轉(zhuǎn)換為一個字符串。1實訓(xùn)分析此函數(shù)返回一個字符指針,因此是一個指針函數(shù)。但該字符指針指向的字符數(shù)組不能為局部變量,在函數(shù)調(diào)用結(jié)束后,局部變量不再存在,所以應(yīng)設(shè)置一個字符數(shù)組為全局變量,作為指針函數(shù)的返回值。將整型數(shù)n轉(zhuǎn)換為一個字符串,需要求出n的每位數(shù)字,并將該數(shù)字轉(zhuǎn)換為字符,

存入字符串str。對n取余,可以取得n的個位數(shù);對n取整再取余,可以取得n的十位數(shù);依次類推,就可以取得n的各位數(shù)字。但是,取得數(shù)字與輸出字符的順序是相反的,例如,取得1234的各位數(shù)字為4、3、2、1。因此,還需將其倒序取出,并轉(zhuǎn)換為字符,存入字符串。下一頁返回6.6實訓(xùn)與實訓(xùn)指導(dǎo)

算法的偽代碼如下:(1)定義全局字符數(shù)組str,

初始化i為0;(2)取出整數(shù)n的各位數(shù)字,

并存入整型數(shù)組a,

while(n):(2.1)a[top++]=n%10;(2.2)n=n/10;(3)將數(shù)組a中的數(shù)據(jù)倒序取出,

存入字符串str,

while(top):(3.1)str[i++]=a[--top];上一頁下一頁返回6.6實訓(xùn)與實訓(xùn)指導(dǎo)

(4)返回str的值。程序的代碼如下:上一頁下一頁返回6.6實訓(xùn)與實訓(xùn)指導(dǎo)

上一頁下一頁返回6.6實訓(xùn)與實訓(xùn)指導(dǎo)

2實訓(xùn)練習(xí)(1)編寫main函數(shù),

測試函數(shù)itos的正確性。(2)設(shè)計一個函數(shù)intstoi(char?str),

將數(shù)字字符串str轉(zhuǎn)換為一個整數(shù)。上一頁下一頁返回6.6實訓(xùn)與實訓(xùn)指導(dǎo)

實訓(xùn)2

日期轉(zhuǎn)換函數(shù)用原型voidgetDate(int?d,int?m,int?y)編寫函數(shù),

從鍵盤讀入一個形如dd-mmm-yyyy的日期。其中,dd表示兩位數(shù)的日,mmm表示月份的3個字母縮寫,yyyy表示四位數(shù)的年份。函數(shù)讀入日期后,將它們轉(zhuǎn)換為整型,然后傳遞給3個參數(shù)。1實訓(xùn)分析題目要求編寫一個函數(shù),形參為整型指針,沒有返回值。函數(shù)的功能包括輸入日期字符串,從中識別出年、月、日,

并通過指針參數(shù)將數(shù)組帶回主調(diào)函數(shù)。

題目的難度在于如何識別3個字母縮寫的月份值,采用的方法是:首先,將12個月的3個字母縮寫的字符串存儲到一個字符串?dāng)?shù)組中,相當(dāng)于建立一張表;然后,把輸入的月份字符串與表中字符串相比較,即可確定月份值。上一頁下一頁返回6.6實訓(xùn)與實訓(xùn)指導(dǎo)

字符串?dāng)?shù)組:char?mons[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};如果當(dāng)前月份為"Nov",那么它在字符串?dāng)?shù)組中的下標(biāo)為10,下標(biāo)增1即月份值。值得注意的是,字符串比較需要調(diào)用庫函數(shù)strcmp。接下來介紹如何將一個數(shù)字字符串s0s1sn轉(zhuǎn)換為整型x。一個數(shù)字字符減去字符'0'為其對應(yīng)的整數(shù)值。

例如,

“'5'-'0'”

的結(jié)果為5。

將x初始化為0,

那么重復(fù)執(zhí)行“x=x?10+si-'0'”n次后,x值就是數(shù)字字符串的整數(shù)值。上一頁下一頁返回6.6實訓(xùn)與實訓(xùn)指導(dǎo)

算法的偽代碼如下:(1)鍵入日期字符串str;(2)遍歷字符串str:(2.1)取前2位字符轉(zhuǎn)換為整數(shù),

存入指針d指向的變量;(2.2)取第4~6位字符轉(zhuǎn)換為字符串mon,

并查表mons來確定月份,

存入指針m指向的變量;(2.3)取第8~11位字符轉(zhuǎn)換為整數(shù),

存入指針y指向的變量。上一頁下一頁返回6.6實訓(xùn)與實訓(xùn)指導(dǎo)

程序的代碼如下:上一頁下一頁返回6.6實訓(xùn)與實訓(xùn)指導(dǎo)

上一頁下一頁返回6.6實訓(xùn)與實訓(xùn)指導(dǎo)

上一頁下一頁返回6.6實訓(xùn)與實訓(xùn)指導(dǎo)

2實訓(xùn)練習(xí)(1)編寫測試函數(shù)getDate的main函數(shù)代碼,

測試函數(shù)的正確性。(2)用原型voidgetTime(int?h,int?m,int?s)編寫函數(shù),

從鍵盤輸入一個形如格式hh:mm:ss[am|pm]的時間表示。

其中,

hh表示兩位的小時數(shù),

mm表示兩位的分鐘數(shù),

ss表示兩位的秒數(shù)。該時間格式既可以是12小時制,也可以是24小時制。12小時制的時間

溫馨提示

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

最新文檔

評論

0/150

提交評論