版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
C語言程序設(shè)計活頁式教程項目6使用指針處理數(shù)據(jù)指針是C語言最具特色的數(shù)據(jù)類型,它極大地豐富了C語言的功能。正確而靈活地使用指針,可以有效地構(gòu)造出復(fù)雜的數(shù)據(jù)結(jié)構(gòu),可以更加方便靈活地操作數(shù)組元素,可以使程序更簡潔、緊湊和高效。指針的概念比較復(fù)雜,初學(xué)者時常會感到較難理解。因此,學(xué)習指針時必須從指針的概念入手,了解什么是指針,如何定義指針變量,指針與其他類型變量的區(qū)別,并掌握指針在數(shù)組、函數(shù)等方面的應(yīng)用。只有在少數(shù)應(yīng)用場景必須用到指針,如申請內(nèi)存空間、釋放內(nèi)存空間、用指針變量代替函數(shù)等情形。而處理普通數(shù)據(jù)、一維數(shù)組、二維數(shù)組時,可以選擇不使用指針。項目任務(wù)知識目標學(xué)習目標任務(wù)1:用指針處理普通類型數(shù)據(jù)任務(wù)2:用指針處理一維數(shù)組中的數(shù)據(jù)任務(wù)3:用指針處理二維數(shù)組中的數(shù)據(jù)任務(wù)4:用指針替代函數(shù)任務(wù)5:用鏈表存儲數(shù)據(jù)(1)了解指針類型和指針變量。(2)掌握指向普通數(shù)據(jù)的指針變量的使用。(3)掌握指向一維數(shù)組的指針變量的使用。(4)掌握指向二維數(shù)組的指針變量的使用。(5)掌握指向函數(shù)的指針變量的使用。(6)掌握使用鏈表存儲無限數(shù)據(jù)。任務(wù)準備任務(wù)實施任務(wù)描述任務(wù)1用指針處理普通類型數(shù)據(jù)任務(wù)描述本任務(wù)通過用指針變量存儲普通類型變量的地址、用指針變量讀取或修改普通類型變量的值、輸出并對比指針變量存儲的地址等子任務(wù),以及任務(wù)準備和任務(wù)拓展中的案例,向?qū)W生講解指針變量的定義和運算操作,讓學(xué)生理解指針類型和指針變量,具備靈活使用指針變量的能力。任務(wù)準備任務(wù)實施Part
1Part
2Part
3任務(wù)描述任務(wù)準備1.指針的概念要理解指針的概念,必須先弄清楚數(shù)據(jù)在計算機內(nèi)存中是如何存儲和讀取的。程序中的一個變量實質(zhì)上代表了“內(nèi)存中的某個存儲單元”。在計算機中,所有的數(shù)據(jù)以及正在運行的程序都是存放在存儲器(簡稱內(nèi)存)中的。內(nèi)存由線性連續(xù)的存儲單元組成,一般把存儲器中的1字節(jié)稱為一個存儲單元,不同的數(shù)據(jù)類型占用的存儲單元數(shù)不同,如int類型的變量占4個單元(有的系統(tǒng)占2個單元),char類型的變量占1個單元等。為了能正確地對存儲單元進行訪問,必須為每個存儲單元編號。根據(jù)存儲單元的編號,能準確地找到該存儲單元,存儲單元的編號也稱地址。由于根據(jù)存儲單元的編號或地址就可以找到所需的存儲單元,所以通常也將這個地址稱為指針。存儲單元的指針和存儲單元的內(nèi)容是兩個不同的概念,可以用一個簡單的例子來說明它們之間的關(guān)系。到賓館去探訪客人時,前臺工作人員將根據(jù)提供的客人名稱或者房間號找到客人。在這里,客人名稱相當于變量名稱,房間號相當于指針,而客人是變量或指針中的內(nèi)容。對于一個存儲單元來說,單元的地址即為指針,其中存放的數(shù)據(jù)才是該單元的內(nèi)容。任務(wù)準備在C語言中,允許用一個變量來存放指針,這種變量稱為指針變量。因此,一個指針變量的值就是某個存儲單元的地址或稱為某個存儲單元的指針。如果在程序中定義了一個變量,在對程序進行編譯時,系統(tǒng)就會給這個變量分配存儲單元。編譯系統(tǒng)會根據(jù)程序中定義的變量類型,分配一定長度的空間,所分配存儲單元的首地址稱為變量的地址,此后,這個變量對應(yīng)的存儲地址也就確定了。若程序需要處理這個變量,就可以通過該地址來處理對應(yīng)的變量。例如,假設(shè)指針tp中存放了字符變量ch的地址,通??尚蜗蟮孛枋鰹閠p指向ch,如圖6-1所示。圖中設(shè)有字符變量ch,其內(nèi)容為'A',ch占用了0022FED4(內(nèi)存地址通常用十六進制數(shù)表示)號單元。設(shè)有指針變量tp,內(nèi)容為0022FED4,這種情況稱為tp指向變量ch,或說tp是指向變量ch的指針。當要訪問變量ch的存儲空間時,可以采用直接訪問的方式(使用ch訪問),還可以通過指針tp來訪問(使用對應(yīng)的運算符)。通過指針tp來訪問ch的存儲空間,其訪問過程是:先訪問指針tp的存儲空間,其中存放的是變量ch的地址,再根據(jù)該地址訪問變量ch的存儲空間。這是對變量的存儲空間訪問的另外一種方式,稱為間接訪問。圖6-1指針變量示意圖tpch0022FED4
'A'0022FED4任務(wù)準備總的來說,指針就是地址,而存儲地址的變量就是指針變量。一個指針變量卻可以在不同時刻被賦予不同的地址。定義指針變量的目的是通過指針變量中的地址訪問存儲單元。既然指針變量的值是一個地址,那么這個地址不僅可以是普通變量的地址,也可以是其他數(shù)據(jù)結(jié)構(gòu)的地址,如一個數(shù)組的首地址或一個函數(shù)的首地址。在C語言中,一種數(shù)據(jù)類型或數(shù)據(jù)結(jié)構(gòu)往往都占有一組連續(xù)的存儲單元。用“地址”這個概念并不能很好地描述一種數(shù)據(jù)類型或數(shù)據(jù)結(jié)構(gòu),而“指針”雖然實際上也是一個地址,但它卻是一個數(shù)據(jù)結(jié)構(gòu)的首地址,它是指向一個數(shù)據(jù)結(jié)構(gòu)的,因而概念更為清楚,表示更為明確。這也是引入“指針”概念的一個重要原因。在C語言中,指針提供了一種間接訪問其他對象(指普通變量、結(jié)構(gòu)體變量、數(shù)組、函數(shù)等)的手段,可以通過為指針變量賦不同的值,使其指向發(fā)生改變。利用這種機制能夠更加靈活方便地實施對各種對象的操作。任務(wù)準備2.指針變量的定義與其它類型的變量一樣,指針變量必須先定義才能使用。指針變量的定義格式如下:類型名*指針變量名;例如:int*tp1;char*tp2;double*tp3;上述變量的定義中,int、char和double都是類型名,tp1、tp2、tp3都是指針變量名,而星號*是定義指針變量必不可少的說明符,說明定義的是指針變量。如果沒有星號*,上述語句就變成了普通變量的定義。int*tp1,*tp2,tp3;同類型的指針變量、普通變量可以一起定義,如上述定義變量的語句中,tp1和tp2是指向int數(shù)據(jù)的指針變量,或者說tp1和tp2是存儲int數(shù)據(jù)的地址的指針變量,而tp3就是存儲int數(shù)據(jù)的普通變量。任務(wù)準備3.指針變量的運算指針變量的運算,是指對指針變量中存儲的地址進行的運算。常見指針變量的運算有:對指針變量賦值;取出指針變量中存儲的地址,再通過地址取出對應(yīng)的存儲單元中的數(shù)據(jù);對指針變量中存儲的地址進行加減運算(移動地址);將指針變量中存儲的地址與其他地址進行比較。(1)指針變量的賦值和取內(nèi)容由于指針變量存儲的是地址,所以給指針變量賦值常常伴隨著取變量的地址,這就必然要用到取地址運算符&。取地址運算符&是單目運算符,它的功能是取變量的地址,在輸入一個整數(shù)的代碼scanf("%d",&x)中已經(jīng)用到過該運算符。取內(nèi)容運算符*也是單目運算符,它的功能是取出某地址對應(yīng)的存儲單元中的數(shù)據(jù)。運算符*后一般跟一個指針變量或一個地址常量(如數(shù)組名)。任務(wù)準備【實例1】定義2個整型變量a和b并輸入值,定義2個整型指針變量tp1和tp2分別指向a和b,用指針變量輸出a和b的值。#include<stdio.h>intmain(){ inta,b; int*tp1=&a,*tp2; tp2=&b; printf("請輸入a和b的值,以逗號隔開:"); scanf("%d,%d",&a,&b); printf("a=%d,b=%d\n",*tp1,*tp2); return1;}編譯運行的結(jié)果如圖6-2所示:在實例1中,有兩處用到了取地址符&,一處是用&取出變量a和b的地址,分別賦值給指針變量tp1和tp2;另一處是用scanf()函數(shù)接收用戶輸入時參數(shù)中出現(xiàn)了&a和&b,可以理解為將用戶輸入的兩個整數(shù)分別存儲到地址&a和&b對應(yīng)的存儲單元中。運算符*后面跟的是一個地址,它的作用是取出該地址對應(yīng)的存儲單元中的數(shù)據(jù)。在實例1中,指針變量tp1和tp2分別存儲變量a和b的地址。因此,*tp1的作用是取出變量a的地址中的數(shù)據(jù),即變量a的值;*tp2的作用是取出變量b的地址中的數(shù)據(jù),即變量b的值。圖6-2指針變量任務(wù)準備(2)指針變量的算術(shù)運算常見的指針變量的算術(shù)運算,是讓指針變量加上或減去一個整數(shù),使指針變量指向相鄰的存儲單元。例如int*tp中,tp是一個指向整型數(shù)據(jù)的指針變量,它記錄的是擁有4個字節(jié)的存儲單元的首地址,因為int類型的數(shù)據(jù)占4個字節(jié)。那么,tp+1、tp+n、tp-1、tp++、--tp等式子都是合法的,它們表示地址向前或向后移動幾個存儲單元。那么,一個指針變量所指向的存儲單元到底是多大呢?占多少個字節(jié)?指針變量加1后地址增加多少?下面以實例2來加以說明。任務(wù)準備【實例2】分別定義char、int、double類型的變量,用指針變量存儲它們的地址,分別輸出指針變量的地址,以及指針變量加1后的地址。#include<stdio.h>intmain(){ charch='a',*tp1=&ch; intx=5,*tp2=&x; doubley=3.14159,*tp3=&y; printf("tp1=%p,tp1+1=%p\n",tp1,tp1+1); printf("tp2=%p,tp2+1=%p\n",tp2,tp2+1); printf("tp3=%p,tp3+1=%p\n",tp3,tp3+1); return1;}編譯運行的結(jié)果如圖6-3所示:圖6-3指針變量的地址值任務(wù)準備在輸出函數(shù)scanf()中,格式%p用于以十六進制輸出一個地址。從在實例2的運行結(jié)果不難看出,char字符型變量ch占用1個字節(jié)的存儲空間,因此它的地址加1(即tp1+1)代表向后移動1個字節(jié)空間。同理,int整型變量x占用4個字節(jié)的存儲空間,x的地址加1(即tp2+1)代表向后移動4個字節(jié)空間,由原先的地址0060FEEC變?yōu)?060FEF0;double雙精度浮點型變量y占用8個字節(jié)的存儲空間,y的地址加1(即tp3+1)代表向后移動8個字節(jié)空間,由原先的地址0060FEE0變?yōu)?060FEE8。由于系統(tǒng)和運行環(huán)境的差異,在不同的計算機上運行實例2所得到的地址結(jié)果會不一樣。有的編譯系統(tǒng)認為int類型只占2個字節(jié),也會導(dǎo)致地址的差值不一樣。綜上所述,指針變量中的地址對應(yīng)的存儲單元的大小,由指針變量的類型來決定。而指針變量進行加減運算后的地址變化,也是以它的類型所占的空間大小為單位的。打個比方,姚朋身高2.3米,潘江身高1.5米,假設(shè)以姚朋、潘江作為數(shù)據(jù)類型來定義指針變量:姚朋*tp1;潘江*tp2;那么,tp1+1代表姚朋向前走一步,由于姚朋身高腿長,他向前走一步的距離是1米;而tp2+1代表潘江向前走一步,跨度只有0.5米。所以說,指針變量進行加減運算后的地址變化,由指針變量的類型決定。任務(wù)準備(3)指針變量的關(guān)系運算與其它類型的變量一樣,指針變量也可以進行關(guān)系運算。假定有兩個指針變量tp1和tp2,它們之間的關(guān)系運算代表的意義如表6-1所示:表6-1指針變量關(guān)系運算的意義關(guān)系運算代表的意義tp1==tp2表示tp1和tp2指向同一空間tp1>tp2表示tp1指向的地址位置更大tp1<tp2表示tp1指向的地址位置更小任務(wù)準備在C語言中,當沒有給某指針變量分配任何有效內(nèi)存地址時,通常用NULL初始化該指針變量,NULL表示空指針常量。因此,常常在訪問任何指針變量之前檢查它是否是空指針,代碼如下所示:intx=10;int*tp1=&x;if(tp1!=NULL){ /*代碼省略*/}在C語言中,判斷指針變量是否為NULL,是指針變量最常見的關(guān)系運算。但將指針變量賦值為NULL與不賦值是不同的。指針變量未賦值時,變量中存儲的地址是一個隨機數(shù),不能直接使用,否則可能造成嚴重錯誤。而當把指針變量賦值為NULL后,則可以使用,只不過該變量不指向任何變量。任務(wù)準備5.指針變量作函數(shù)的參數(shù)和返回值函數(shù)的參數(shù)不僅可以是整型、浮點型、字符型、數(shù)組等類型,也可以是指針類型。當整型、浮點型、字符型數(shù)據(jù)作函數(shù)的參數(shù)時,傳入函數(shù)的是數(shù)值的副本(即將數(shù)據(jù)復(fù)制一份傳入函數(shù)中),這就是我們常說的“傳值”。而當指針、數(shù)組類型的數(shù)據(jù)作函數(shù)的參數(shù)時,傳入函數(shù)的是地址的副本(即將地址復(fù)制一份傳入函數(shù)中),這就是我們常說的“傳引用”。(1)函數(shù)參數(shù)傳值以傳值的形式將數(shù)據(jù)的副本傳入函數(shù),并在函數(shù)中修改這個數(shù)據(jù)副本,對函數(shù)外的原數(shù)據(jù)沒有影響,因為函數(shù)修改的是原數(shù)據(jù)的副本。任務(wù)準備【實例3】定義一個函數(shù)swap()用于交換兩個整型變量的值。在main()函數(shù)中調(diào)用該函數(shù),檢驗swap()函數(shù)是否能將兩個整型變量的值交換。#include<stdio.h>voidswap(inta,intb);intmain(){ intx=17,y=8; printf("調(diào)用swap()前,x=%d,y=%d\n",x,y); swap(x,y); printf("調(diào)用swap()后,x=%d,y=%d\n",x,y); return1;}voidswap(inta,intb){ printf("swap()內(nèi)交換前,a=%d,b=%d\n",a,b); intc; c=a; a=b; b=c; printf("swap()內(nèi)交換后,a=%d,b=%d\n",a,b);}編譯運行的結(jié)果如圖6-4所示:圖6-4函數(shù)參數(shù)傳值任務(wù)準備從在實例3的運行結(jié)果不難看出,調(diào)用swap()函數(shù)并沒有將實際參數(shù)x和y的值交換,只是在swap()函數(shù)內(nèi)部,形式參數(shù)a和b的值被交換了。也可以理解為,變量x的副本a與變量y的副本b發(fā)生了交換,但變量x和變量y本身并沒有發(fā)生交換。實例3的執(zhí)行過程如圖6-5所示,從圖示可以看出,實際參數(shù)x和y在調(diào)用swap()函數(shù)前后的值沒變化,發(fā)生交換的只是形式參數(shù)a和b的值。圖6-5參數(shù)傳值示意圖任務(wù)準備(2)函數(shù)參數(shù)傳址傳址是指將地址常量或變量(即指針)作為一個函數(shù)的參數(shù)。如果一個函數(shù)的形式參數(shù)是指針變量,那么在調(diào)用該函數(shù)時,實際參數(shù)可以是地址常量(如數(shù)組名)、變量的地址(如&x)或指針變量。以傳址的形式將數(shù)據(jù)所在的地址的副本傳入函數(shù),并在函數(shù)中通過取內(nèi)容運算符*來修改地址副本指向的原數(shù)據(jù),就能夠修改函數(shù)外的原數(shù)據(jù)。這就好比有人將你家房門鑰匙復(fù)制一份,用復(fù)制的鑰匙同樣能夠打開房門進入你家,能將你家中的物品洗劫一空。任務(wù)準備【實例4】定義一個函數(shù)swap()代入兩個整型變量的地址,實現(xiàn)交換變量的值。在main()函數(shù)中調(diào)用該函數(shù),檢驗swap()函數(shù)是否能將兩個整型變量的值交換。#include<stdio.h>voidswap(int*a,int*b);intmain(){ intx=17,y=8; printf("調(diào)用swap()前,x=%d,y=%d\n",x,y); swap(&x,&y); printf("調(diào)用swap()后,x=%d,y=%d\n",x,y); return1;}voidswap(int*a,int*b){ printf("swap()內(nèi)交換前,*a=%d,*b=%d\n",*a,*b); intc; c=*a; *a=*b; *b=c;printf("swap()內(nèi)交換后,*a=%d,*b=%d\n",*a,*b);}編譯運行的結(jié)果如圖6-6所示:圖6-6函數(shù)參數(shù)傳址任務(wù)準備從在實例4的運行結(jié)果不難看出,通過將變量x和y的地址傳入swap()函數(shù)后,可以在swap()函數(shù)內(nèi)部修改變量x和y的值,從而實現(xiàn)了它們值的交換。實例4的執(zhí)行過程如圖6-7所示,從圖示可以看出,變量x和y在調(diào)用swap()函數(shù)后的值發(fā)生了交換。這里假設(shè)變量x的地址是FE8C,假設(shè)變量y的地址是FED4。圖6-7參數(shù)傳址示意圖任務(wù)準備(3)函數(shù)返回指針在C語言中,函數(shù)的返回值類型不僅可以是諸如int、char、double之類的簡單數(shù)據(jù)類型,還可以是指針類型。當一個函數(shù)的返回值是內(nèi)存地址的時候,我們稱這個函數(shù)為指針函數(shù)。指針函數(shù)的定義格式如下:數(shù)據(jù)類型*函數(shù)名(形式參數(shù)列表){ ……}例如下面定義的fun()函數(shù),它返回一個指向整型數(shù)據(jù)的指針變量??梢岳斫鉃閺脑摵瘮?shù)返回的地址開始的4個字節(jié)中,存儲的是一個整型數(shù)據(jù)。int*fun(intn){ int*p; /*代碼省略*/ returnp;}任務(wù)準備C語言中的數(shù)組能存儲同類型的批量數(shù)據(jù),但不足的是,數(shù)組一旦定義,它的元素個數(shù)就不能改變。因此,對于數(shù)據(jù)個數(shù)不確定的場合,數(shù)組是不適用的。而接下來的實例5中的input()函數(shù)就能實現(xiàn)根據(jù)欲存儲數(shù)據(jù)的個數(shù)來申請內(nèi)存空間,返回空間的首地址。實例5中的input()函數(shù)的功能是,根據(jù)欲存儲的整數(shù)個數(shù)n來申請內(nèi)存空間,并將該內(nèi)存空間的首地址作為返回值返回。申請內(nèi)存空間需要用到庫函數(shù)malloc(),它在頭文件malloc.h中定義,用法如下:(數(shù)據(jù)類型*)malloc(個數(shù)*sizeof(數(shù)據(jù)類型));例如: int*p=(int*)malloc(5*sizeof(int));malloc()函數(shù)的作用是,在內(nèi)存的動態(tài)存儲區(qū)中分配一個指定長度的連續(xù)空間,并返回分配區(qū)域的起始地址。sizeof是C語言中的一個操作符,它的作用是返回一個類型所占的內(nèi)存字節(jié)數(shù)。sizeof(int)返回4。上述代碼的作用是,申請5個int數(shù)據(jù)所占據(jù)的內(nèi)存空間,即20字節(jié)的連續(xù)空間,將連續(xù)空間的起始地址強制轉(zhuǎn)換為int*指針類型后,賦值給指針變量p。任務(wù)準備【實例5】定義一個函數(shù)int*input(intn),要求用戶輸入n個整數(shù)并存儲,返回這n個整數(shù)的首地址。在main()函數(shù)中要求用戶輸入欲輸入的整數(shù)個數(shù),調(diào)用函數(shù)input()實現(xiàn)這批整數(shù)的存儲,然后再輸出。#include<malloc.h>int*input(intn);intmain(){ intx,i,*tp; printf("請輸入整數(shù)個數(shù):"); scanf("%d",&x); tp=input(x); printf("輸入的所有整數(shù):"); for(i=0;i<x;i++) printf("%d,",*(tp+i)); return1;}int*input(intn){ inti; int*p=(int*)malloc(n*sizeof(int)); for(i=0;i<n;i++) { printf("請輸入第%d個整數(shù):",i+1); scanf("%d",p+i);/*p+i是地址*/ } returnp;}任務(wù)準備編譯運行的結(jié)果如圖6-8所示:從在實例5的運行結(jié)果不難看出,input()函數(shù)申請了能存儲5個整數(shù)的連續(xù)空間,并將該空間的首地址返回給main()函數(shù)中的指針變量tp,再通過tp將存儲的5個整數(shù)全部輸出。圖6-8返回指針的函數(shù)任務(wù)準備任務(wù)實施Part
1Part
2Part
3任務(wù)描述任務(wù)實施【任務(wù)1】定義一個整型變量并賦初值,定義一個指針變量存儲整型變量的地址,并輸出指針變量所指向的整型數(shù)據(jù)。1.任務(wù)分析本任務(wù)考查最基礎(chǔ)的指針變量操作,包括賦值和取內(nèi)容。任務(wù)實施2.任務(wù)實現(xiàn)實現(xiàn)代碼如下,請將代碼中空白處補充完整。#include<stdio.h>intmain(){ intx=10,*tp; tp=
; printf("tp=
,*tp=
\n",tp,*tp); printf("tp+1=%p,*(tp+1)=%d\n",tp+1,*(tp+1)); return1;}編譯運行的結(jié)果如圖6-9所示:圖6-9指向整型數(shù)據(jù)的指針任務(wù)實施3.任務(wù)總結(jié)本任務(wù)將變量x的地址&x賦值給指針變量tp,然后用*tp取出地址中的數(shù)據(jù),即變量x的值。將指針變量的值以十六進制地址的形式輸出,可以看出tp+1的地址比tp的地址大4,剛好是一個int數(shù)據(jù)所占據(jù)的空間。這充分說明int*類型的指針變量加1,意味著地址向前移動4個字節(jié)。輸出結(jié)果6356728是變量x后面4個字節(jié)中的數(shù)據(jù),它是不確定的,不同的編譯環(huán)境會取到不同的數(shù)值。此處*(tp+1)是向大家展示,通過指針變量可以訪問未經(jīng)編譯器分配的地址中的數(shù)據(jù),這種做法是不安全的,也是不提倡的。任務(wù)準備任務(wù)實施任務(wù)描述任務(wù)2用指針處理一維數(shù)組中的數(shù)據(jù)任務(wù)描述本任務(wù)通過用指針變量來訪問一維數(shù)組中的元素,向?qū)W生講解用指針變量訪問一維數(shù)組元素的各種形式,讓學(xué)生具備靈活處理批量數(shù)據(jù)的能力。任務(wù)準備任務(wù)實施Part
1Part
2Part
3任務(wù)描述任務(wù)準備1.指針變量訪問一維數(shù)組元素數(shù)組是一種存儲批量相同類型數(shù)據(jù)的構(gòu)造類型。一維數(shù)組中的元素按順序存放在地址連續(xù)的內(nèi)存單元中,每一個數(shù)組元素都有各自的地址,而數(shù)組名就是數(shù)組的首地址。對于數(shù)組元素,可以使用“數(shù)組名[下標]”形式訪問,也可以通過指針變量訪問。不同之處在于,數(shù)組名是地址常量,而指針變量是存儲地址的變量。一維數(shù)組和指針的定義如下:inta[5]={11,22,33,44,55};int*tp=a;doubleb[5]={1.23,2.34,3.45,4.56,5.67};double*tp2=&b[0];對數(shù)組a的第i個元素的訪問,可以通過以下形式:a[i];tp[i];*(tp+i);*tp++; /*先*tp取出地址中的數(shù)據(jù),再tp++將地址加1,指向下一個數(shù)組元素*/*(a+i);任務(wù)準備在將數(shù)組名a賦值給指針變量tp后,由于a和tp都是數(shù)組的首地址,所以a與tp幾乎可以通用。唯獨*a++在編譯時會報錯,這是因為a是常量,不能執(zhí)行a++操作。通常使用一維字符數(shù)組存儲一個字符串常量,而指針變量又可以存儲一維數(shù)組的首地址,因此字符串常量的存儲可以用以下兩種方式:charch[]="abcdefg";char*cp="abcdefg";但對于存儲用戶輸入的字符串的情形,就只能先定義一維字符數(shù)組并分配空間,再接收用戶輸入的字符串。這種情形下不能使用字符指針變量,因為指針變量只能存儲一個地址,它并沒有足夠的空間來存放用戶輸入的字符串。兩種代碼如下:charch[200];char*cp;scanf("%s",ch); /*正確做法,ch有分配空間*/scanf("%s",cp); /*錯誤做法,cp沒有分配空間*/任務(wù)準備【實例1】定義一個整型數(shù)組和一個整型指針變量,用多種形式輸出數(shù)組中的元素。intmain(){ inta[5]={11,22,33,44,55}; int*tp,i; tp=a; for(i=0;i<5;i++) printf("%d,",a[i]); printf("\n"); for(i=0;i<5;i++) printf("%d,",tp[i]); printf("\n"); for(i=0;i<5;i++) printf("%d,",*(tp+i)); printf("\n"); for(i=0;i<5;i++) printf("%d,",*tp++); printf("\n"); return0;}編譯運行的結(jié)果如圖6-10所示:從運行結(jié)果可以看出,在循環(huán)中用a[i]、tp[i]、*(tp+i)、*tp++、*(a+i)均可依次取出數(shù)組中的元素。數(shù)組名b是數(shù)組的首地址,元素b[0]是數(shù)組的第0個元素,對b[0]取地址后,&b[0]就是第0個元素的地址,也是數(shù)組的首地址。所以對于一維數(shù)組b來說,b與&b[0]是等價的。圖6-10指向整型數(shù)組的指針任務(wù)準備【實例2】定義一個double數(shù)組和一個double指針變量,用指針變量輸出數(shù)組中的元素。intmain(){ inti; doubleb[5]={1.23,2.34,3.45,4.56,5.67}; double*tp2=b; for(i=0;i<5;i++) printf("%.2lf,",b[i]); printf("\n"); tp2=&b[0]; for(i=0;i<5;i++) printf("%.2lf,",b[i]); printf("\n"); return0;}編譯運行的結(jié)果如圖6-11所示:從運行結(jié)果可以看出,給指針變量tp2賦值b或&b[0],均能正確輸出數(shù)組b中的元素,充分證明一維數(shù)組名b與&b[0]是等價的。圖6-11指向double型數(shù)組的指針任務(wù)準備2.一維數(shù)組的地址關(guān)系對于一維整型數(shù)組a來說,a、a+1、&a[0]、&a[0]+1、&a、&a+1都表示地址,它們又有什么不同呢?下面我們用實例3來說明這些地址的不同之處。任務(wù)準備【實例3】定義一個整型數(shù)組a,輸出a、a+1、&a[0]、&a[0]+1、&a、&a+1的地址,比較它們的不同之處。#include<stdio.h>intmain(){ inta[10]={1,2,3,4,5,6,7,8,9,0}; printf("a=%p\n",a); printf("a+1=%p\n",a+1); printf("&a[0]=%p\n",&a[0]); printf("&a[0]+1=%p\n",&a[0]+1); printf("&a=%p\n",&a); printf("&a+1=%p\n",&a+1); return0;}編譯運行的結(jié)果如圖6-12所示:圖6-12數(shù)組的各種地址任務(wù)準備需要注意的是,因為編譯環(huán)境不同,運行后輸出的地址也會不同。本實例中,數(shù)組名a的地址是0060FEC8,a+1的地址是0060FECC,兩者相差4,即一個int數(shù)據(jù)所占據(jù)的字節(jié)數(shù)。說明a+1是下一個元素a[1]的地址。第0個元素的地址&a[0]是0060FEC8,與a相同;&a[0]+1是0060FECC,與a+1相同。&a[0]與&a[0]+1的地址也相差4,說明&a[0]+1與a+1都是元素a[1]的地址。這也間接說明對于一維數(shù)組a來說,a與&a[0]等價。對數(shù)組名a取地址,得到&a是0060FEC8,與a、&a[0]相等。但&a+1是0060FEF0,與a+1、&a[0]+1不同。計算&a+1與&a的差值,發(fā)現(xiàn)兩者相差40,而40個字節(jié)剛好是10個int數(shù)據(jù)占據(jù)的空間。說明&a+1與&a相比,跨過了10個int數(shù)據(jù)的存儲空間。綜上所述,可以將一維數(shù)組的相關(guān)地址進行分類,a、a+1、&a[0]、&a[0]+1記錄的是數(shù)組元素的地址,對a或&a[0]進行加減運算時,地址的跨度是一個數(shù)組元素所占空間的倍數(shù)。而&a、&a+1記錄的是整個數(shù)組的地址(可以理解為一行數(shù)據(jù)的地址),對&a進行加減運算時,地址的跨度是整個數(shù)組所占空間的倍數(shù)。例如本實例中整型數(shù)組a有10個元素,與&a相比,&a+1的地址跨度剛好是10個int數(shù)據(jù)占據(jù)的空間,即40個字節(jié)。任務(wù)準備任務(wù)實施Part
1Part
2Part
3任務(wù)描述任務(wù)實施【任務(wù)1】在main()函數(shù)中定義一個整型數(shù)組并初始化,定義并調(diào)用函數(shù)將一維整型數(shù)組中最小的數(shù)與第一個數(shù)對換,把最大的數(shù)與最后一個數(shù)對換。然后輸出一維數(shù)組的所有元素。1.任務(wù)分析本任務(wù)采用指針變量來訪問數(shù)組元素。先通過指針變量p訪問數(shù)組中的n個元素,指針變量max和min記錄數(shù)組最大值、最小值所在的地址,再通過取內(nèi)容運算符*取出地址中的數(shù)據(jù),最后用臨時變量temp實現(xiàn)最小的數(shù)與第一個數(shù)、最大的數(shù)與最后一個數(shù)的對換。任務(wù)實施2.任務(wù)實現(xiàn)實現(xiàn)代碼如下,請將代碼中空白處補充完整。#include<stdio.h>voidduihuan(int*p,intn);voidshuchu(int*p,intn);intmain(){ inta[10]={59,15,-100,-76,96,22,38,75,6,34}; printf("對換前:"); shuchu(a,10); duihuan(
,
); printf("\n對換后:"); shuchu(a,10); return0;}voidduihuan(int*p,intn){ inti,*max=p,*min=p,temp; for(i=0;i<n;i++) { /*max記錄最大值的地址*/ if(
>*max) max=
; /*min記錄最小值的地址*/ if(
<*min) min=
; } temp=*p;*p=*min;*min=temp; temp=*(p+n-1);*(p+n-1)=*max;*max=temp;}voidshuchu(int*p,intn){ inti; for(i=0;i<n;i++) printf("%d,",*(p+i));}任務(wù)實施編譯運行的結(jié)果如圖6-13所示:從運行結(jié)果來看,最小值-100與第一個數(shù)59成功對換,最大值96與最后一個數(shù)34成功對換。圖6-13利用指針實現(xiàn)數(shù)據(jù)元素對換任務(wù)實施3.任務(wù)總結(jié)將數(shù)組首地址賦值給指針變量p后,p+i就是就是數(shù)組第i個元素的地址,*(p+i)就能取出數(shù)組第i個元素的值。任務(wù)實施【任務(wù)2】統(tǒng)計一個字符串中的整數(shù),存儲到整型數(shù)組中,然后輸出所有整數(shù)。1.任務(wù)分析假如有“a123x45617960?3025ab5876”這個字符串,內(nèi)有數(shù)字和非數(shù)字字符,將其中連續(xù)的數(shù)字作為一個整數(shù),依次存放到整型數(shù)組a中。例如123放在a[0],456放在a[1]…以此類推。統(tǒng)計共有多少個整數(shù),并輸出這些數(shù)。統(tǒng)計字符串中的整數(shù)需要設(shè)置一個標記變量flag,當字符是數(shù)字時將flag設(shè)為1,不是數(shù)字時設(shè)為0。當本字符是數(shù)字時,只需要將字符對應(yīng)的數(shù)字累加到整數(shù)num上,并將flag設(shè)為1。當本字符不是數(shù)字而上一個字符是數(shù)字時,說明是一個整數(shù)的結(jié)束,這時就要將整數(shù)num存儲到整型數(shù)組a中,并將flag設(shè)為0。當字符串結(jié)束時,如果結(jié)束符'\0'之前的字符是數(shù)字,那么也要將整數(shù)num存儲到整型數(shù)組a中。任務(wù)實施2.任務(wù)實現(xiàn)實現(xiàn)代碼如下,請將代碼中空白處補充完整。intmain(){ char*str="a123x45617960?3025ab5876"; inta[50];/*分別是計數(shù)、整數(shù)值、標志位、循環(huán)變量*/ intcount=0,num=0,flag=0,i; charch=*str; while(ch!=
) { if(
) /*是數(shù)字*/ { num=10*num+(
); flag=1; } else /*非數(shù)字*/ {/*如果上一個字符是數(shù)字*/ if(flag==1) {/*將整數(shù)存入數(shù)組,并計數(shù)*/ a[
]=num; } flag=0;/*重新開始存儲下一個整數(shù)*/ num=
; } str++; /*指向下一個字符*/ ch=*str; } if(flag==1)/*如果'\0'之前是數(shù)字*/ a[count++]=num; printf("共%d個整數(shù):\n",count); for(i=0;i<count;i++) printf("%d\t",a[i]); return0;}任務(wù)實施編譯運行的結(jié)果如圖6-14所示:從運行結(jié)果來看,字符串“a123x45617960?3025ab5876”中的5個整數(shù)均被正確存儲和輸出。圖6-14統(tǒng)計字符串中的整數(shù)任務(wù)實施3.任務(wù)總結(jié)當程序中有字符串常量作為初始值時,編譯系統(tǒng)會分配內(nèi)存空間來存儲該字符串常量,然后將內(nèi)存空間的首地址賦值給指針變量或數(shù)組名。數(shù)字9與字符'9'是類型不同的兩個常量,它們的整型數(shù)值分別是9和57。將字符'9'轉(zhuǎn)換成對應(yīng)的數(shù)字9,可以用字符'9'減去字符'0',它們的差值就是整型數(shù)值9。將字符串“123”變成整數(shù)123的做法是,先將變量num設(shè)初值為0,然后循環(huán)訪問字符串中每一個字符。每次訪問一個字符時,都是把變量num乘以10再加上該字符對應(yīng)的整數(shù)值,這樣三次循環(huán)后num的取值分別是1、12、123。任務(wù)準備任務(wù)實施任務(wù)描述任務(wù)3用指針處理二維數(shù)組中的數(shù)據(jù)任務(wù)描述本任務(wù)通過用指針變量來訪問二維數(shù)組中的元素,向?qū)W生講解用指針變量訪問二維數(shù)組元素的各種形式,以及與二維數(shù)組相關(guān)的各類地址的寫法,讓學(xué)生具備靈活處理批量數(shù)據(jù)的能力。任務(wù)準備任務(wù)實施Part
1Part
2Part
3任務(wù)描述任務(wù)準備1.二維數(shù)組地址和元素的訪問形式二維數(shù)組實質(zhì)上也是一個一維數(shù)組,只不過它的每一個元素又是一個一維數(shù)組,即二維數(shù)組可以看作是由若干個一維數(shù)組組成的。二維數(shù)組的邏輯結(jié)構(gòu)如表6-2所示,可以看出二維數(shù)組a的每一行就是一個一維數(shù)組。表6-2二維數(shù)組的邏輯結(jié)構(gòu)任務(wù)準備下面定義一個3行5列的二維整型數(shù)組并初始化:inta[3][5]={ {1,2,3,4,5}, {6,7,8,9,10}, {11,12,13,14,15}};該二維數(shù)組常見地址和元素的訪問形式,如表6-3所示:表6-3二維數(shù)組地址和元素的訪問形式訪問形式數(shù)據(jù)類型含義a行地址第0行的地址,等同&a[0]a+1行地址第1行的地址,等同&a[1]*a元素地址第0行第0列元素的地址,等同&a[0][0]、a[0]*a+1元素地址第0行第1列元素的地址,等同&a[0][1]、a[0]+1*(a+1)元素地址第1行第0列元素的地址,等同&a[1][0]、a[1]*(a+1)+1元素地址第1行第1列元素的地址,等同&a[1][1]、a[1]+1**a元素值第0行第0列元素的值,等同a[0][0],值為1**(a+1)元素值第1行第0列元素的值,等同a[1][0],值為6*(*(a+1)+1)元素值第1行第1列元素的值,等同a[1][1],值為7&a二維數(shù)組地址3行5列二維數(shù)組的首地址,對該地址進行算術(shù)運算,跨度是3行5列共15個int數(shù)據(jù)所占據(jù)的空間,即60個字節(jié)。&a+1二維數(shù)組地址3行5列二維數(shù)組的首地址往后移3行5列得到的地址任務(wù)準備【實例1】定義一個3行5列的二維整型數(shù)組并初始化,用多種形式輸出該二維數(shù)組相關(guān)的地址和元素值。intmain(){ inta[3][5]={ {1,2,3,4,5}, {6,7,8,9,10}, {11,12,13,14,15} }; printf("a指第0行的地址:%p,&a[0]=%p\n",a,&a[0]); printf("a+1指第1行地址:%p,&a[1]=%p\n",a+1,&a[1]); printf("*a指第0行第0列元素地址:%p,&a[0][0]=%p\n",*a,&a[0][0]); printf("*a+1指第0行第1列元素地址:%p,&a[0][1]=%p\n",*a+1,&a[0][1]); printf("*(a+1)指第1行第0列元素地址:%p,&a[1][0]=%p\n",*(a+1),&a[1][0]); printf("*(a+1)+1指第1行第1列元素地址:%p,&a[1][1]=%p\n",*(a+1)+1,&a[1][1]); printf("**a指第0行第0列元素值:%d,a[0][0]=%d\n",**a,a[0][0]); printf("**(a+1)指第1行第0列元素值:%d,a[1][0]=%d\n",**(a+1),a[1][0]); printf("*(*(a+1)+1)指第1行第1列元素的值:%d,a[1][1]=%d\n",*(*(a+1)+1),a[1][1]); printf("&a指3行5列二維數(shù)組的首地址:%p\n",&a); printf("&a+1指數(shù)組首地址往后移3行5列得到的地址:%p\n",&a+1); return0;}任務(wù)準備編譯運行的結(jié)果如圖6-15所示:圖6-15實例1運行結(jié)果任務(wù)準備將運行結(jié)果中的訪問形式和對應(yīng)數(shù)值填入表6-4中,并對數(shù)據(jù)作分析對比,找出不同訪問形式之間的規(guī)律。需要說明的是,不同的編譯環(huán)境得到的地址是不一樣的。表6-4訪問形式和對應(yīng)數(shù)值訪問形式數(shù)據(jù)類型數(shù)值備注a行地址0060FEB4
a+1行地址0060FEC8地址與a相差20個字節(jié),即5個int數(shù)據(jù)占據(jù)的空間。*a元素地址0060FEB4
*a+1元素地址0060FEB8地址與*a相差4個字節(jié),即1個int數(shù)據(jù)占據(jù)的空間。*(a+1)元素地址0060FEC8
*(a+1)+1元素地址0060FECC地址與*(a+1)相差4個字節(jié),即1個int數(shù)據(jù)占據(jù)的空間。**a元素值1
**(a+1)元素值6
*(*(a+1)+1)元素值7
&a二維數(shù)組地址0060FEB4地址與a相同,但代表的維度層級不一樣,運算跨度不一樣。&a+1二維數(shù)組地址0060FEF0地址與&a相差60個字節(jié),即3*5個int數(shù)據(jù)占據(jù)的空間。任務(wù)準備從上表可以看出,二維數(shù)組名a是第0行的首地址,其它地址都是對數(shù)組名a進行運算而得來的。a表示二維數(shù)組第0行的首地址。a+1表示向前跨一行,是第1行的地址。*a表示進入第0行內(nèi)部,是第0行第0列元素的地址。同理,*(a+1)表示進入第1行內(nèi)部,是第1行第0列元素的地址。*a+1表示地址*a向前跨過一個元素,是第0行第1列元素的地址。同理,*(a+1)+1表示地址*(a+1)向前跨過一個元素,是第1行第1列元素的地址。**a表示進入元素地址*a的內(nèi)部取出數(shù)據(jù),即第0行第0列元素的值。**(a+1)表示進入元素地址*(a+1)的內(nèi)部取出數(shù)據(jù),即第1行第0列元素的值。*(*(a+1)+1)表示進入元素地址*(a+1)+1的內(nèi)部取出數(shù)據(jù),即第1第第1列元素的值。&a表示跳出a所在行的維度(一行的跨度為5個int數(shù)據(jù)占據(jù)的空間),進入擁有3行5列共15個元素的維度(即整個二維數(shù)組所在的維度),它是該3行5列二維整型數(shù)組的首地址。&a+1表示地址&a向前跨過一個擁有3行5列共15個元素的二維整型數(shù)組,它與地址&a的差值是60,剛好就是15個int數(shù)據(jù)占據(jù)的空間,即3*5*4個字節(jié)。任務(wù)準備對比以上描述可以看出,二維數(shù)組的維度層級依次是:整個二維數(shù)組地址、二維數(shù)組的行地址、行內(nèi)元素的地址、元素的值,它們分別對應(yīng)&a、a、*a、**a。從實例1的輸出結(jié)果可以看出,a+i是一個行地址(第i行的地址),*(a+i)+j是一個元素地址(第i行第j列元素的地址),*(*(a+i)+j)則是一個元素值(第i行第j列元素的值)。由此我們可以得出結(jié)論:取內(nèi)容運算符*的作用是進入下一層的維度,取地址運算符&的作用是跳出現(xiàn)有維度進入上一層的維度。而對地址進行算術(shù)運算所導(dǎo)致的地址跨度,取出于該地址所在的維度。例如,二維數(shù)組名a是第0行的首地址,那么*a就是第0行第0列元素的地址,**a就是第0行第0列元素的值,&a就是整個二維數(shù)組的首地址。雖然&a的地址值與a相同,但對它們進行算術(shù)運算所導(dǎo)致的地址跨度不同。由于&a是一個3行5列的二維整型數(shù)組的首地址,所以&a+1相對于&a的地址跨度是一個3行5列二維整型數(shù)組所占據(jù)的字節(jié)數(shù),即3*5*4=60。又由于a是一個有5個元素一維整型數(shù)組(即第0行)的首地址,所以a+1相對于a的地址跨度是5個整型元素所占據(jù)的字節(jié)數(shù),即5*4=20。任務(wù)準備2.行指針行指針變量用于存儲一行數(shù)組元素的起始地址。二維數(shù)組的每一行可以看作是一個一維數(shù)組,因此可以定義一個行指針變量來指向二維數(shù)組中的某一行。聲明行指針變量的格式為:數(shù)據(jù)類型(*指針變量)[n];如用式子char(*cp)[10]定義的指針變量cp,它是一個指向擁有10個元素的一維字符數(shù)組的變量,它是該一維字符數(shù)組的首地址。cp+1相對cp的跨度是10個字符占據(jù)的字節(jié)數(shù)。任務(wù)準備假設(shè)有二維整型數(shù)組a和一個指向擁有5個元素的一維整型數(shù)組的行指針變量tp,它們的定義如下:inta[3][5]={ {1,2,3,4,5}, {6,7,8,9,10}, {11,12,13,14,15}};int(*tp)[5];可以將a、a+1、&a[0]、&a[1]賦值給行指針變量tp,因為它們都表示某一行的地址。代碼如下:tp=a;tp=a+1;tp=&a[0];tp=&a[1];任務(wù)準備另外還有一個“指針數(shù)組”的概念,它與行指針的定義相似,但意義完全不同。指針數(shù)組實質(zhì)上還是一個數(shù)組,只不過該數(shù)組的元素類型是指針,數(shù)組的每個元素都用來存儲地址,它的定義格式如下:數(shù)據(jù)類型*數(shù)組名[n];如用式子double*d[10]定義一個數(shù)組d,它是一個擁有10個元素的一維數(shù)組,數(shù)組的每個元素都存儲指向double數(shù)據(jù)的地址。元素d[i]返回一個地址,該地址指向的存儲單元占8個字節(jié),里面存放的是double數(shù)據(jù)。任務(wù)準備任務(wù)實施Part
1Part
2Part
3任務(wù)描述任務(wù)實施【任務(wù)1】定義一個3行5列的二維整型數(shù)組并初始化,再定義一個指向擁有5個元素的一維整型數(shù)組的行指針變量,用行指針變量輸出二維整型數(shù)組中的所有元素。1.任務(wù)分析本任務(wù)先讓行指針變量tp指向二維數(shù)組a的第0行,再通過行指針變量來獲取二維數(shù)組的所有元素值并輸出。本任務(wù)嘗試用多種訪問形式來表示第0行的地址和第i行第j列元素,讓學(xué)生能更靈活地使用行指針變量訪問二維數(shù)組。任務(wù)實施2.任務(wù)實現(xiàn)本任務(wù)實現(xiàn)代碼如下,請將代碼中空白處補充完整。#include<stdio.h>intmain(){ inti,j; inta[3][5]={ {1,2,3,4,5}, {6,7,8,9,10}, {11,12,13,14,15} }; int(*tp)[5]; tp=
; /*或tp=&a[0]; */ for(i=0;i<3;i++) { for(j=0;j<5;j++) {/*用tp表示第i行第j列元素*/ printf("%d,",
); /*printf("%d,",*(tp[i]+j));*/ /*printf("%d,",tp[i][j]);*/ } printf("\n"); } return0;}編譯運行的結(jié)果如圖6-16所示:可以修改實現(xiàn)代碼,使用*(*(tp+i)+j)、*(tp[i]+j)、tp[i][j]中的任何一個,都可以正確輸出二維數(shù)組的所有元素。圖6-16指向二維數(shù)組的指針任務(wù)實施3.任務(wù)總結(jié)在任務(wù)1中將二維數(shù)組第0行的首地址a賦值給行指針變量tp后,二維數(shù)組的第i行第j列元素的訪問形式有:*(*(tp+i)+j) 或 *(*(a+i)+j)*(tp[i]+j) 或 *(a[i]+j)tp[i][j] 或 a[i][j]因此,當把二維數(shù)組名a賦值給行指針變量tp后,tp可以代替a來訪問數(shù)組元素。在C語言中,二維數(shù)組和行指針還可以作為函數(shù)的參數(shù),還可以用指向二維數(shù)組元素的指針變量來訪問二維數(shù)組的所有元素。下面以兩個拓展任務(wù)為例,來學(xué)習一下這兩種用法。任務(wù)實施【任務(wù)2】分別用二維整型數(shù)組、行指針變量作函數(shù)參數(shù),輸出二維數(shù)組的全部元素。1.任務(wù)分析用二維數(shù)組作函數(shù)參數(shù)時,代入的是第0行的首地址,形式參數(shù)只需指明一行有多少列元素。用行指針變量作函數(shù)參數(shù)時,代入的也是某行的首地址,形式參數(shù)一樣需要指明一行有多少列元素。任務(wù)實施2.任務(wù)實現(xiàn)本任務(wù)實現(xiàn)代碼如下,請將代碼中空白處補充完整。#include<stdio.h>voidshuchu1(intb[][5],intn);voidshuchu2(int(*p)[5],intn);intmain(){ inta[3][5]={ {1,2,3,4,5}, {6,7,8,9,10}, {11,12,13,14,15} }; shuchu1(a,3); printf("------------------\n"); shuchu2(
,
); return0;}voidshuchu1(intb[][5],intn)/*只需給出列數(shù)為5*/{ inti,j; for(i=0;i<n;i++) { for(j=0;j<5;j++) { printf("%d,",b[i][j]); } printf("\n"); }}任務(wù)實施voidshuchu2(int(*p)[5],intn){ inti,j; for(i=0;i<n;i++) { for(j=0;j<5;j++) { printf("%d,",
); } printf("\n"); }}編譯并運行代碼,輸出結(jié)果如圖6-17所示:圖6-17用指針輸出二維數(shù)組元素任務(wù)實施3.任務(wù)總結(jié)無論是二維數(shù)組作函數(shù)參數(shù),還是行指針作函數(shù)參數(shù),代入的都是某一行的地址。而且參數(shù)必須指定一行有多少個元素,因為這直接決定了一行的跨度是多大。本任務(wù)中的形式參數(shù)intb[][5]和int(*p)[5]就指定了一行有5個元素,而行的數(shù)量則由參數(shù)n決定。任務(wù)實施【任務(wù)3】定義一個整型指針變量,讓它指向二維整型數(shù)組第0行第0列的元素,然后用該指針變量輸出二維數(shù)組的全部元素。1.任務(wù)分析為了便于理解,二維數(shù)組中的元素被人為地分成幾行幾列。事實上,二維數(shù)組中的數(shù)據(jù)在內(nèi)存中是連續(xù)存儲的,各元素并沒有行列之分。因此,也可以用一個指向二維數(shù)組第0行第0列元素的指針變量,逐個訪問二維數(shù)組中的所有元素。任務(wù)實施2.任務(wù)實現(xiàn)本任務(wù)實現(xiàn)代碼如下,請將代碼中空白處補充完整。#include<stdio.h>intmain(){inta[3][5]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; inti,*tp=
; for(i=0;i<
;i++) { if(i%5==0) printf("\n"); printf("%d,",
); } return0;}編譯并運行代碼,輸出結(jié)果如圖6-18所示:圖6-18指向二維數(shù)組元素的指針任務(wù)實施3.任務(wù)總結(jié)二維數(shù)組在初始化時,可以將初始數(shù)據(jù)分成多行寫,也可以像一維數(shù)組一樣寫在一行。指針變量tp是指向整型數(shù)據(jù)的指針變量,所以它可以被賦值為二維數(shù)組元素的地址。對tp進行算術(shù)運算時,地址的跨度也僅僅是一個數(shù)組元素所占據(jù)的字節(jié)數(shù)。由于二維數(shù)組中的元素在內(nèi)存中是連續(xù)存儲的,當tp被賦值為第0行第0列元素的地址后,可以通過tp指向二維數(shù)組中的所有元素,進而取出元素值。任務(wù)準備任務(wù)實施任務(wù)描述任務(wù)4用指針替代函數(shù)任務(wù)描述本任務(wù)通過定義指向函數(shù)的指針變量,實現(xiàn)用指針變量代替函數(shù),以及將函數(shù)作為另一個函數(shù)的參數(shù)。任務(wù)準備任務(wù)實施Part
1Part
2Part
3任務(wù)描述任務(wù)準備1.返回指針值的函數(shù)函數(shù)的返回值類型可以是整型、浮點型、字符型等普通類型,也可以是指針類型,即函數(shù)返回的數(shù)據(jù)是一個地址。通常把返回指針類型的函數(shù)稱作“指針函數(shù)”,它的聲明格式如下:數(shù)據(jù)類型*函數(shù)名(參數(shù)列表)例如聲明返回整型數(shù)組最大值地址的函數(shù),格式如下:int*maxAddr(inta[],intn);任務(wù)準備【實例1】定義一個返回整型數(shù)組最大值地址的函數(shù),在main()函數(shù)中調(diào)用并輸出最大值。#include<stdio.h>int*maxAddr(inta[],intn);intmain(){ int*tp; inta[]={55,-87,14,101,23,42,77,50}; tp=maxAddr(a,8); printf("最大值:%d\n",*tp); return0;}int*maxAddr(inta[],intn){ int*p=a,i; for(i=0;i<n;i++) { if(a[i]>*p) p=a+i; } returnp;}編譯運行的結(jié)果如圖6-19所示:在函數(shù)maxAddr()中始終用指針變量p記錄最大值的地址,最后返回該指針變量。函數(shù)maxAddr()就是一個返回指針值的函數(shù),簡稱為指針函數(shù)。圖6-15實例1運行結(jié)果任務(wù)準備2.指向函數(shù)的指針與數(shù)組一樣,函數(shù)經(jīng)系統(tǒng)編譯后,它的目標代碼在內(nèi)存中連續(xù)存放。函數(shù)名本身就是一個地址,是函數(shù)的入口地址。在C語言中,指針變量除了可以指向普通變量和數(shù)組外,還可以指向函數(shù),即指針變量也可以存儲函數(shù)的地址。我們通常把指向函數(shù)的指針變量稱為“函數(shù)指針”,它的定義格式為:類型名(*指針變量名)(參數(shù)列表)這里有兩個需要區(qū)分的概念:指針函數(shù)和函數(shù)指針。指針函數(shù)本質(zhì)上是一個函數(shù),只不過該函數(shù)的返回類型是指針;函數(shù)指針本質(zhì)上是一個指針變量,只不過該指針變量指向函數(shù)。任務(wù)準備某指向函數(shù)的指針變量定義如下:int(*fp)(intx,doubley);或:int(*fp)(int,double);上述聲明函數(shù)指針的代碼中,定義了一個指向函數(shù)的指針變量fp,它所指向的函數(shù)有一個int型參數(shù)和一個double型參數(shù),函數(shù)返回類型為int。也就是說,指針變量fp可以存儲類似intfun(intx,doubley)形式函數(shù)的地址。指向函數(shù)的指針變量的聲明和調(diào)用代碼如下:int(*fp)(intx,doubley); /*聲明指針函數(shù)*/intfun(intx,doubley); /*聲明函數(shù)*/fp=fun; /*將函數(shù)名賦值給指針變量*/inta=(*fp)(15,3.14); /*通過指針變量調(diào)用函數(shù)fun*/任務(wù)準備【實例2】定義一個指向函數(shù)的指針變量,讓該變量分別指向自定義函數(shù)add()和sub(),從而實現(xiàn)對兩個整數(shù)的相加、相減運算。#include<stdio.h>intadd(inta,intb);intsub(inta,intb);intmain(){ intx=15,y=5,z; int(*tp)(inta,intb); tp=add; z=(*tp)(x,y); printf("tp=add時,z=%d\n",z); tp=sub; z=(*tp)(x,y); printf("tp=sub時,z=%d\n",z); return0;}intadd(inta,intb){ returna+b;}intsub(inta,intb){ returna-b;}任務(wù)準備編譯運行的結(jié)果如圖6-20所示:從運行結(jié)果來看,指向函數(shù)的指針變量tp確實可以分別代替函數(shù)add()和sub(),來實現(xiàn)兩個整數(shù)的加和減。如果將參數(shù)不同的intfun(intx,doubley)賦值給指針變量tp,tp是否依然可以代替函數(shù)fun()呢?答案是:系統(tǒng)會給出編譯警告,但并不報錯,程序運行無法得到正確結(jié)果。圖6-20實例2運行結(jié)果任務(wù)準備任務(wù)實施Part
1Part
2Part
3任務(wù)描述任務(wù)實施【任務(wù)1】定義函數(shù)實現(xiàn)一維整型數(shù)組的冒泡排序,要求排序規(guī)則由代入的指向函數(shù)的指針變量決定。任務(wù)實施1.任務(wù)分析我們已經(jīng)學(xué)習過冒泡排序的原理和實現(xiàn)。對一維整型數(shù)組進行冒泡排序,代碼如下:voidmaopao2(inta[],intn){ inttemp,i,j; for(i=0;i<n;i++) { for(j=0;j<n-1-i;j++) { /*從大到小排序*/ if(a[j]<a[j+1]) { temp=a[j]; a[j]=a[j+1]; a[j+1]=temp; } } }}函數(shù)maopao2()要求代入一維整型數(shù)組的首地址和元素個數(shù),而排序的順序由a[j]與a[j+1]的大小決定。當a[j]<a[j+1]時,相鄰的兩個數(shù)交換,說明是從大到小排序,當a[j]>a[j+1]時,相鄰的兩個數(shù)交換,說明是從小到大排序。任務(wù)實施從上面的分析來看,在調(diào)用maopao2()函數(shù)進行排序前,必須按排序規(guī)則修改maopao2()函數(shù)中的代碼。但這種每當排序規(guī)則改變時就去修改排序函數(shù)代碼的做法,違背了軟件工程中“高內(nèi)聚,低耦合”的思想。通常程序結(jié)構(gòu)中各模塊的內(nèi)聚程度越高,模塊間的耦合程度就越低,程序模塊的可重用性、移植性就更強。簡單來說,就是函數(shù)與函數(shù)之間不要有太多關(guān)聯(lián),不能因為排序規(guī)則變了就不得不修改排序函數(shù)的代碼。因此,我們提倡將排序規(guī)則作為參數(shù)代入到排序函數(shù)中。本任務(wù)就是先創(chuàng)建排序規(guī)則的函數(shù),再用指向函數(shù)的指針變量將排序規(guī)則函數(shù)代入排序函數(shù)中,從而實現(xiàn)按代入的規(guī)則來排序。任務(wù)實施2.任務(wù)實現(xiàn)本任務(wù)實現(xiàn)代碼如下,請將代碼中空白處補充完整。#include<stdio.h>voidmaopao(inta[],intn,int(*com)(int,int));voidshuchu(inta[],intn);intxiaoda(inta,intb);intdaxiao(inta,intb);intchu5(inta,intb);intmain(){ intb[]={58,94,12,7,45,75,25,56,43}; /*排序規(guī)則由代入的xiaoda決定,而不需要去修改maopao()函數(shù)的代碼*/ maopao(b,9,
); /*maopao(b,9,daxiao);*/ /*maopao(b,9,chu5);*/ shuchu(b,9); return1;}任務(wù)實施/*改進后的冒泡排序,排序規(guī)則從參數(shù)代入*/voidmaopao(inta[],intn,
){ inttemp,i,j; for(i=0;i<n;i++) { for(j=0;j<n-1-i;j++) { /*if(a[j]>a[j+1])*/ if(
==1){ /*交換規(guī)劃由com()函數(shù)決定*/ temp=a[j]; a[j]=a[j+1]; a[j+1]=temp; } } }}/*輸出一維數(shù)組元素*/voidshuchu(inta[],intn){ inti; for(i=0;i<n;i++) printf("%d,",a[i]);}/*從小到大排序,即a>b時交換*/intxiaoda(inta,intb){ intc=0; /*a>b就交換,說明是從小到大排序*/ if(a>b) c=1; returnc;}任務(wù)實施/*從大到小排序,即a<b時交換*/intdaxiao(inta,intb){ intc=0; if(a<b) c=1; returnc;}/*比較a,b除以5后的余數(shù)大小,按余數(shù)從大到小排序*/intchu5(inta,intb){ intc=0; inta1=a%5; intb1=b%5; if(a1<b1) c=1; returnc;}任務(wù)實施編譯運行的結(jié)果如圖6-21所示:改進后的冒泡排序函數(shù)的聲明和調(diào)用如下:/*聲明函數(shù)*/voidmaopao(inta[],intn,int(*com)(int,int));maopao(b,9,xiaoda); /*調(diào)用函數(shù)*/而冒泡排序的規(guī)則從由a[j]與a[j+1]的大小來決定,變成了由變量com所指向的函數(shù)的返回值來決定,也就是說maopao()函數(shù)把排序的規(guī)則交給了代入的函數(shù)來決定。后續(xù)如果出現(xiàn)其它排序規(guī)則,只需要按指定格式創(chuàng)建一個新函數(shù),將排序規(guī)則寫在該函數(shù)中,最后作為參數(shù)代入maopao()函數(shù),即可實現(xiàn)按新規(guī)則排序,而不需要修改maopao()函數(shù)的代碼。圖6-21任務(wù)1運行結(jié)果任務(wù)實施例如,如果要比較一維整型數(shù)組元素除以5后的余數(shù)大小,并按余數(shù)從大到小排序,則可以定義如下函數(shù):intchu5(inta,intb){ intc=0; inta1=a%5; intb1=b%5; if(a1<b1) c=1; returnc;}調(diào)用maopao()函數(shù)排序時,只需將該函數(shù)名作參數(shù)代入即可:maopao(b,9,chu5);編譯運行的結(jié)果如圖6-22所示:圖6-22按chu5()規(guī)則的排序結(jié)果數(shù)據(jù){94,58,43,12,7,56,45,75,25}除以5后的余數(shù)為{4,3,3,2,2,1,0,0,0}。從運行結(jié)果來看,排序規(guī)則確實是各個整數(shù)除以5后的余數(shù)從大到小排序。任務(wù)實施3.任務(wù)總結(jié)本任務(wù)用指針變量代替指定排序規(guī)則的函數(shù),巧妙地將實現(xiàn)交換動作的代碼與指定排序規(guī)則的代碼剝離,函數(shù)maopao()只負責寫元素交換的代碼,而函數(shù)xiaoda()、daxiao()、chu5()
溫馨提示
- 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)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024版房屋修建承包合同范本
- 專用機械設(shè)備運輸協(xié)議2024版版A版
- 二零二五年度智能化建筑系統(tǒng)集成與勘測合同范本3篇
- 2025年打印機網(wǎng)絡(luò)安全協(xié)議3篇
- 2024版美容院員工勞動協(xié)議范本版B版
- 2024年高效食堂管理及餐飲服務(wù)承包合同書一
- 2024高端牙科美容服務(wù)定制合同
- 2024版鑄鐵部件供應(yīng)協(xié)議樣本版B版
- 武漢體育學(xué)院《中學(xué)化學(xué)教材分析》2023-2024學(xué)年第一學(xué)期期末試卷
- 二零二五年度綠色節(jié)能型家裝水電施工總承包合同范本3篇
- 2020年上海市高考英語二模試卷(a卷)
- 對賬單標準模板
- 小學(xué)科學(xué)教科版四年級下冊第二單元《電路》復(fù)習教案(2023春新課標版)
- 創(chuàng)業(yè)計劃書(成人用品店)
- 電機的結(jié)構(gòu)及工作原理
- GB 6245-2006消防泵
- 空調(diào)維修保養(yǎng)服務(wù)突發(fā)事件應(yīng)急處置方案
- 東岸沖沙閘及進水閘施工方案
- 寵物入住酒店免責協(xié)議
- 2022年滬教版(全國)九年級化學(xué)下冊第6章溶解現(xiàn)象章節(jié)測試試卷(精選含答案)
- 河南省地圖含市縣地圖矢量分層地圖行政區(qū)劃市縣概況ppt模板
評論
0/150
提交評論