




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
第9章指針[Return]本章學習目標
掌握指針和指針變量的概念掌握指針變量的定義及運算掌握指針處理數(shù)組的方法掌握指針處理字符串的方法掌握函數(shù)指針的概念和含有指針參數(shù)的函數(shù)的定義及調用方法本章主要內容9.1指針與內存地址9.2變量的指針與指針變量9.3數(shù)組指針9.4字符串指針9.5函數(shù)指針9.6返回指針值的函數(shù)9.7指針數(shù)組和指向指針的指針9.1指針與內存地址9.1.1變量的地址在定義一個變量時,系統(tǒng)首先會在內存中為這個變量分配一塊存儲區(qū)用來存放這個變量的值。而這塊存儲區(qū)的大小是由變量類型所決定的。例如,在C系統(tǒng)中一個int型數(shù)據(jù)占2個字節(jié),因此當我們定義一個int型變量時,系統(tǒng)就會為這個變量分配一個大小為2個字節(jié)的存儲區(qū)。為了方便管理這些存儲區(qū),操作系統(tǒng)會給內存中的每一個字節(jié)進行編號,這個編號就叫做內存的“地址”,而每一個變量所占據(jù)存儲區(qū)的第一個字節(jié)的編號稱為是該變量的“地址”
例如,我們定義三個整型變量:intm,n,k;定義時,假設系統(tǒng)為這三個變量分配存儲空間的情況如圖9-1所示。由于int類型在C系統(tǒng)中占2個字節(jié),所以在定義m,n,k三個變量時,系統(tǒng)將編號為1000和1001的兩個字節(jié)分配給m,將1002、1003兩個字節(jié)分配給n,將1004、1005兩個字節(jié)分配給k。因此變量m的地址就是1000,變量n的地址是1002,變量k的地址是1004。mnk……100010021004圖9-1系統(tǒng)分配地址……9.1.2變量在內存中的存取1、變量在內存中的存儲系統(tǒng)對變量值的存取實際都是通過地址進行的。例如:m=20;n=25;k=30;程序在執(zhí)行這三條賦值語句時,系統(tǒng)會根據(jù)變量名m、n、k的地址(1000、1002、1004)找到它們在內存中所對應的存儲區(qū),然后將數(shù)值20、25、30分別存放到這些存儲區(qū)中,從而完成了賦值操作。如圖9-2mnk……100010021004圖9-2賦值后的內存情況……2025302、從變量中讀取數(shù)據(jù)從變量中讀取已存的數(shù)據(jù)也是通過地址進行的。例如,程序在執(zhí)行printf(“%d”,m);時,系統(tǒng)會根據(jù)變量名m找到它所對應的地址1000,然后從編號為1000、1001的兩個字節(jié)的存儲區(qū)中取出所存放的數(shù)據(jù)20,然后將它輸出到控制臺中。由此可見,系統(tǒng)通過變量地址就可以方便地對變量單元進行訪問(這種方式也稱為“直接訪問”方式),因此我們可以形象地說變量地址“指向”了該變量單元。在C語言中,也將地址叫做“指針”,變量的地址就稱為變量的“指針”。[Return]9.2 變量的指針與指針變量9.2.1什么是指針變量訪問某一變量單元還可以通過一種“間接訪問”的方式,可以先將某一變量的地址存放到另外一個特殊的變量中,然后通過訪問這個特殊變量中所存放的地址從而得到該地址所指向的變量單元中的值。例如:intm=3;假設系統(tǒng)為變量m分配的存儲空間的地址為2000,現(xiàn)定義一個特殊變量p,將變量m的地址(2000)存放到p中。如圖9-3所示。20003pm圖9-3變量p的存儲圖由于特殊變量p中存放的是m的地址2000,所以通過訪問p就可以間接得到m的值3。這樣就好象在特殊變量p和變量m之間建立了一種聯(lián)系,即通過p可以得到m的地址,從而得到m的值。這種聯(lián)系一般稱為“指向”,特殊變量p稱為“指針變量”,p和m的關系就可以稱為指針變量p指向變量m。指針變量:專門用來存放另一變量的地址(既指針)的變量。※注意:指針變量只能存放變量的地址(既指針),而不能存放其他類型的數(shù)據(jù)。9.2.2指針變量的定義在使用指針變量前必須要先定義,指針變量的定義格式為:指針變量所指變量的類型
﹡指針變量名定義格式中“﹡”表示所定義的變量是一個指針變量,例如:int﹡p;表示p是一個指向整型變量的指針變量,既只能存放整型變量的地址,而不能存放其他類型的地址。如果一行定義多個指針變量,不同的指針變量名之間要用“,”隔開。例如:char﹡p1,﹡p2,﹡p3;※注意:c語言的使用者和一些資料經(jīng)常將定義指針變量簡化說成定義指針,實際指針和指針變量是兩個完全不同的概念:指針是指地址,是某種特定類型的數(shù)據(jù)在內存中的存放地址;指針變量是指存放指針(既地址)的變量。因此,對于平時所說的“定義一個指針”就是指定義一個指針變量。9.2.3指針變量的引用對指針變量進行賦值和引用主要通過“&”和“﹡”兩個運算符實現(xiàn)的。1.“&”運算符“&”運算符又叫做取地址運算符,它是單目運算符,可以返回某一變量的內存地址,常用于對指針變量進行賦值的操作中。例如: intm; int﹡p; p=&m;
/*將變量m的地址獲取并賦給指針變量p,表示p指向變量m*/賦值后的示意圖如圖9-4所示。
&mpm圖9-4取變量m地址在對指針變量進行賦值時應該注意以下幾點:(1)由于指針變量存放的是地址(指針),因此不能將其他非地址類型的數(shù)據(jù)賦給指針變量。例如以下的賦值不合法: int﹡p; p=100;/*錯誤,不能將一個整型數(shù)據(jù)賦給指針變量*/(2)對指針變量進行賦值時,注意不要將“p=&m”寫成“﹡p=&m”。因為“﹡p=&m”不是將m的地址賦給p,而是將m的地址賦給指針變量p所指的變量。(3)對指針變量進行賦值時,指針變量的數(shù)據(jù)類型應該與指針變量中所要存放地址的變量的類型相同。例如,如果定義“char﹡q”,則q只能指向字符類型的變量,而不能指向其他類型的變量。例如: intn; char﹡q; q=&n;/*錯誤,指針變量q只能存放字符類型變量的地址*/2.“﹡”運算符“﹡”運算符又叫做“間接訪問”或“取地址內容”運算符,該運算符也是單目運算符,它與指針變量結合,用來表示該指針變量所指變量的值。/*exam9-1*/#include“stdio.h”main(){intm;int﹡p;m=5;p=&m;/*將m的地址賦給p*/printf(“m=%d\n”,m);printf(“﹡p=%d\n”,﹡p);} /*
﹡p代表的就是p所指的變量單元m的值*/[演示]/*exam9-2*/#include“stdio.h”main(){intm;int﹡p;m=5;p=&m;/*將m的地址賦給p*/﹡p=10;
/*將10賦給p所指的變量單元*/printf(“m=%d\n”,m);printf(“﹡p=%d\n”,﹡p);} 本程序中,在將5賦給變量m后,雖然沒有直接改變m的值,但由于指針變量p指向m,因此程序第5行改變﹡p的值實際就是改變p所指變量m的值,即m的輸出結果為10。[演示]需要說明的是,當一個指針變量沒有指向任何存儲空間時(即沒有對指針變量進行賦值),不能使用“﹡”運算符來進行指針取內容運算。例如: #include“stdio.h” main() { int﹡p; printf(“%d”,﹡p);/*錯誤,因為p沒有指針任何存儲空間*/ } 在以上的程序中,由于指針變量p沒有指向任何的存儲空間,即﹡p并不存在,因此直接引用輸出﹡p是錯誤的。但如果運行以上的錯誤程序,讀者會發(fā)現(xiàn)該程序不但可以正常運行,而且輸出的結果會是一串數(shù)字。實際上這串數(shù)字結果是毫無意義的,它只是一個隨機數(shù),這是因為我們雖然沒有給指針變量p進行賦值,但C語言系統(tǒng)在進行編譯的時候會隨機地賦給指針變量p一個地址值,因此輸出的這個數(shù)字結果是一個沒有任何意義的值。3.“&”和“﹡”的優(yōu)先級關系“&”和“﹡”的優(yōu)先級相同,它們與“++”是同一級別,僅次于“()”,因此當它們在一起使用的時候,按自右而左的方向進行結合運算。例如,假設指針變量p已指向變量m(p=&m),則運行&﹡p時,先進行﹡p的運算,即變量m,然后再進行&運算。因此&﹡p運算就等價于&m運算,表達式返回的值就是變量m的地址。同樣,當執(zhí)行﹡&m時,先執(zhí)行&m,即返回變量m的地址,然后再進行*運算,最后表達式返回的值就是變量m的值。/*exam9-3*/#include“stdio.h”main(){intm;int﹡p;m=10;p=&m;printf(“m=%d,﹡p=%d,﹡&m=%d\n”,m,﹡p,﹡&m);printf(“&m=%x,p=%x,&﹡p=%x\n”,&m,p,&﹡p);} } [演示]m=10,*p=10,*&m=10&m=12ff7c,p=12ff7c,&*p=12ff7c程序運行結果:﹡p和﹡&m的值就是變量m的值;p和&﹡p的值就是變量m的地址指針變量應用舉例例9-4通過指針變量實現(xiàn)將兩個整型數(shù)按照從小到大的順序輸出/*exam9-4*/#include“stdio.h”main(){intm,n;int﹡p,﹡p1,﹡p2;scanf(“%d,%d”,&m,&n);/*從鍵盤輸入m和n的值*/p1=&m;p2=&n;if(m>n)p=p1;p1=p2;p2=p;/*交換指針變量p1、p2所指向的對象*/printf(“m=%d,n=%d\n”,m,n);printf(“﹡p1=%d,﹡p2=%d\n”,﹡p1,﹡p2);}[演示]15,10↙m=15,n=10*p1=10,*p2=15程序運行結果:例9-4程序分析:本程序中,p1指向變量m,p2指向變量n。當輸入m=15,n=10時,由于m>n,所以執(zhí)行“p=p1;p1=p2;p2=p”,將指針變量p1與p2的值進行交換。在這里請注意,p1與p2交換的只是它們所指向變量的地址(即交換完后p1指向變量n,p2指向變量m),而變量m與n的值卻沒有改變。因此在輸出﹡p1和﹡p2時,實際輸出的是變量n和m的值。指針變量p1與p2交換過程如圖9-4所示。10&m&n15指針變量p1指針變量p2變量m變量np1與p2交換前(a)&m10&n15指針變量p1指針變量p2變量m變量np1與p2交換后(b)圖9-4指針變量p1與p2交換示意圖例9-4通過指針變量實現(xiàn)將兩個整型數(shù)按照從小到大的順序輸出/*exam9-4*/#include“stdio.h”main(){intm,n;int﹡p,﹡p1,﹡p2;scanf(“%d,%d”,&m,&n);
/*從鍵盤輸入m和n的值*/p1=&m;p2=&n;if(m>n)p=p1;p1=p2;p2=p;/*交換指針變量p1、p2所指向的對象*/printf(“m=%d,n=%d\n”,m,n);printf(“﹡p1=%d,﹡p2=%d\n”,﹡p1,﹡p2);}15,10↙m=10,n=15*p1=10,*p2=15程序運行結果:對于指針變量p,當直接引用p時,代表的是p所指向變量的地址;當引用﹡p時,代表的是p所指向的變量的值。9.2.3指針變量的引用在第七章討論函數(shù)的調用方式時,我們介紹了函數(shù)的參數(shù)可以是整型、實型、字符型等類型的數(shù)據(jù),這種函數(shù)的調用一般被稱為“傳值調用”。除了“傳值調用”的函數(shù)外,C語言還提供了一種調用函數(shù)的方式——“傳址調用”,這種函數(shù)的特點是參數(shù)的類型是指針類型?!皞髦嫡{用”的函數(shù)和“傳址調用”的函數(shù)在形式上和功能上有什么區(qū)別呢?我們先來看兩個例子。例9-6通過“傳值調用”的方式,實現(xiàn)交換兩個變量m、n的值/*exam9-6*/#include“stdio.h”voidswap(intx,inty){inttemp;temp=x;x=y;y=temp;}main(){intm,n;m=5;n=10;swap(m,n);/*將m,n的值作為實參傳遞給swap函數(shù)*/printf(“m=%d,n=%d\n”,m,n);}m=5,n=10程序運行結果:[演示]程序分析:本程序的意圖是通過swap(intx,inty)函數(shù)實現(xiàn)對變量m和n進行值交換,但是在輸出結果中m和n的值卻沒有改變。這是因為在函數(shù)調用時,m的值傳給x,n的值傳給y。在執(zhí)行完“temp=x;x=y;y=temp;”后,互換的是x和y的值,main()函數(shù)中的m和n的值卻沒有互換。由此可見,“傳值調用”中的單向傳送的值傳遞方式并不能將形參值的改變傳遞給實參值。例9-6中的參數(shù)傳遞示意圖如圖9-5所示。551010mxnym、n的值傳遞給x、y(a)圖9-5“傳值調用”參數(shù)傳遞示意圖510執(zhí)行完swap函數(shù)后各變量的值(b)105mnxy因此,要想使被調函數(shù)中改變了的變量值能夠被主調函數(shù)所用,就不能采用“傳值調用”的函數(shù)方法,而應該將指針變量作為函數(shù)參數(shù),即在函數(shù)的調用過程中將變量的地址作為實參值傳遞給函數(shù),這樣如果在函數(shù)執(zhí)行過程中指針變量所指向的變量值發(fā)生了變化,那么當函數(shù)調用完畢后,主調函數(shù)的這些變量值的變化仍舊保留下來,這樣就實現(xiàn)了通過被調函數(shù)修改主調函數(shù)中變量的值。這種采用指針變量作為形參的函數(shù)調用方法也稱為“傳址調用”。例9-7通過“傳址調用”的方式,實現(xiàn)交換兩個變量m、n的值/*exam9-7*/#include“stdio.h”voidswap(int﹡p,int﹡q)/*swap函數(shù)功能是交換指針變量p,q所指變量的值*/{inttemp;temp=﹡p;﹡p=﹡q;﹡q=temp;}main(){intm,n;int﹡p1,﹡p2;m=5;n=10;p1=&m;/*指針變量pointer_1指向變量m*/p2=&n;/*指針變量pointer_1指向變量n*/swap(p1,p2);/*將m,n的地址作為實參傳遞給swap函數(shù)*/printf(“m=%d,n=%d\n”,m,n);}[演示]程序分析:main函數(shù)中定義了兩個整型變量m和n,定義了兩個整型指針變量p1和p2。p1指向變量m,p2指向變量n,如圖9-6(a)所示。p1,p2做為實參將它們所存放的m和n的地址傳遞給swap函數(shù)的參數(shù)p和q(此時p和q存放的是變量m和n的地址),如圖9-6(b)所示。當swap函數(shù)中的“temp=*p;*p=*q;*q=temp”執(zhí)行后,進行交換的是p和q所指向的變量的值,即變量m和n的值進行互換,如圖9-6(c)所示。&n&m5&n10p1mp2n(a)圖9-6指針變量做參數(shù)實現(xiàn)交換m和n的值&m5p1m&mp10p2n&n(b)&m10&n5p1mp2n(c)q因此,如果想通過被調函數(shù)來改變主調函數(shù)中變量的值,可以:(1)如果主調函數(shù)需要改變n個變量的值,那么在被調用函數(shù)中就相應地定義n個指針形參。(2)在主調函數(shù)調用被調函數(shù)時,主調函數(shù)將需要改變值的n個變量的地址作為實參傳給被調用函數(shù)。(3)被調函數(shù)執(zhí)行時,通過修改被調函數(shù)的指針形參的值從而改變主調函數(shù)中相應變量的值。例9-8輸入三個整數(shù),然后找出并輸出最小的整數(shù)/*exam9-8*/#include“stdio.h”voidgetMin(int﹡pt1,int﹡pt2,int﹡pt3,int﹡pt){﹡pt=﹡pt1;if(﹡pt>﹡pt2) ﹡pt=﹡pt2;elseif(﹡pt>﹡pt3) ﹡pt=﹡pt3;}main(){inta,b,c,min;int﹡p1,﹡p2,﹡p3,﹡p;scanf(“%d%d%d”,&a,&b,&c);/*輸入三個整數(shù)*/p1=&a;p2=&b;p3=&c;p=&min;getMin(p1,p2,p3,p);/*調用getMin函數(shù)找出最小整數(shù)*/printf(“最小整數(shù)為:%d”,min);/*輸出最小整數(shù)min的值*/}[演示][Return]9.3 數(shù)組指針我們知道數(shù)組是由若干個數(shù)組元素組成的,而每個數(shù)組元素在內存中又有相應的地址,因此指針變量除了可以指向一般的變量外,也可以指向數(shù)組或數(shù)組元素。指針與數(shù)組有著密切的聯(lián)系,使用指針的移動不但可以方便地訪問數(shù)組元素還可以使程序在運行效率上有著顯著地提高。9.3.1指向數(shù)組元素的指針指向數(shù)組元素指針的方法與指向普通變量指針的方法相同。例如: inta[5]; int﹡p; p=&a[0];該程序中定義了一個整型數(shù)組a和整型指針變量p,將數(shù)組元素a[0]
的地址賦給指針變量p,p就指向了數(shù)組a的第一個元素。如圖9-7所示。a[0]a[1]a[2]a[3]a[4]&a[0]p圖9-7指向一維數(shù)組的指針變量※注意:指針變量的類型必須要和數(shù)組類型一致,即如果數(shù)組為int類型,則指針變量也必須為int類型。在C語言中,同時規(guī)定數(shù)組名在使用時代表數(shù)組首元素的地址,因此p=&a[0]等價于p=a,即p指向數(shù)組第一個元素。和普通的指針變量一樣,在定義指向數(shù)組元素的指針變量的同時也可以對它賦初值。例如例9-9中的程序就等價于: inta[5]; int﹡p=&a[0];※注意:千萬不要把“p=a”認為是p指向a整個數(shù)組,在這里只是將數(shù)組a的首元素地址賦給了p。9.3.2通過指針引用數(shù)組元素引用數(shù)組中的元素除了可以使用常見的下標法之外,還可以使用指針法。在C語言中規(guī)定,如果指針變量p指向數(shù)組中的某一個元素,則p+1就指向該數(shù)組的下一個元素。例如,如果指針變量p指向數(shù)組a的第一個元素a[0],則p+1就指向下一個元素a[1],p+2指向數(shù)組元素a[2]。依次類推,p+i就指向數(shù)組元素a[i]。因此我們可以得出:(1)&a[i],p+i,a+i均表示a數(shù)組的第i個元素的地址。注意,這里的p+i不是將p的值簡單地加i,而是在p所指元素的地址上加上i個數(shù)組元素所占的字節(jié)數(shù)。例如,假設數(shù)組a是一個整型數(shù)組,則p+i所代表的地址就為p+i*2(2為一個整型元素所占的字節(jié)數(shù)),即p+i指向數(shù)組的第i個元素a[i]。(2)a[i],﹡(p+i),﹡(a+i)均表示a數(shù)組的第i個元素的值。由于p+i,a+i均表示a[i]元素的地址,因此﹡(p+i),﹡(a+i)就代表的是元素a[i]的值。這種引用數(shù)組元素的方法一般也稱為指針法。例9-10分別使用下標法和指針法輸出數(shù)組中各元素的值/*exam9-10*/#include“stdio.h”main(){inta[10];inti,﹡p;for(i=0;i<10;i++)scanf(“%d”,&a[i]);printf(“\n”);for(i=0;i<10;i++)printf(“%d”,a[i]);/*利用數(shù)組下標法輸出數(shù)組中的元素*/printf(“\n”);for(i=0;i<10;i++)printf(“%d”,﹡(a+i));/*利用指針法輸出10個元素的值,﹡(a+i)等價于a[i]*/printf(“\n”);for(p=a;p<(a+10);p++)printf(“%d”,﹡p);/*利用指針法輸出10個元素的值,通過指針變量p的移動*//*來依次輸出數(shù)組中的10個元素*/} [演示]在此需要注意的是:(1)使用下標法和使用﹡(a+i)的執(zhí)行效率是相同的,因為當使用下標法訪問數(shù)組元素時,C語言的編譯系統(tǒng)會將a[i]自動轉換為﹡(a+i)進行處理,即每次都需要計算元素地址,因此在執(zhí)行起來比較費時。而使用指針變量直接指向數(shù)組元素,然后通過p++的移動來訪問各元素的方法則不需要計算地址,因此執(zhí)行效率大大提高。(2)當通過指針法來訪問數(shù)組元素時,注意不能使用a++來移動指針變量,因為a是數(shù)組名,它代表數(shù)組的首地址,是一個常量,因此無法實現(xiàn)自加操作。例9-11使用指針法輸出數(shù)組中各元素的值/*exam9-11*/#include“stdio.h”main(){inta[10];inti,﹡p;for(p=a;p<(a+10);p++)scanf(“%d”,p);printf(“\n”);for(i=0;i<10;i++,p++)printf(“%d”,﹡p);}[演示]從程序運行結果可以發(fā)現(xiàn)最后輸出的并不是我們期望的數(shù)組a的10個元素的值,而是毫無意義的一組數(shù)字。這是因為當程序執(zhí)行完第一個for循環(huán)后,指針變量p指向的是數(shù)組a的最后一個元素a[9],而并非是第一個元素a[0]。因此在執(zhí)行第二個for循環(huán)的時候,p++的值就超出了數(shù)組的范圍,從而使指針變量p指向了一組不可預料的存儲單元,導致最后輸出結果是一組毫無意義的數(shù)字。要解決這個問題,我們只需要在程序執(zhí)行完第一個for循環(huán)后,再讓p指向數(shù)組a的第一個元素就可以了。見程序9-12。例9-12/*exam9-12*/#include“stdio.h”main(){inta[10];inti,﹡p;for(p=a;p<(a+10);p++)scanf(“%d”,p);
p=a;/*令指針變量p指向數(shù)組a的一個元素*/for(i=0;i<10;i++,p++)printf(“%d”,﹡p);}[演示]9.3.3數(shù)組名作函數(shù)參數(shù)我們在第7章“函數(shù)的調用”時曾討論過,當使用數(shù)組名作函數(shù)的參數(shù)時,如果形參數(shù)組元素的值發(fā)生變化,實參數(shù)組元素的值也會隨之變化。例如:例9-13利用數(shù)組名作函數(shù)參數(shù)實現(xiàn)對10個整數(shù)進行排序/*exam9-13*/#include“stdio.h”voidsort(intarray[]){ inti,j;intt,k;for(i=0;i<9;i++){k=i;for(j=i+1;j<10;j++){if(array[j]<array[k])k=j;} t=array[k];array[k]=array[i];array[i]=t;}}
main(){inta[10];inti;printf(“pleaseinput10numbers:\n”);for(i=0;i<10;i++)scanf(“%d”,&a[i]);printf(“\n”);sort(a);/*將數(shù)組名a作為實參傳給函數(shù)sort進行排序*/printf(“thearrayhasbeensorted:\n”);for(i=0;i<10;i++)/*輸出排序后的數(shù)組a中的各元素*/printf(“%d”,a[i]);}pleaseinput10numbers:9876543210↙thearrayhasbeensorted:0123456789程序運行結果:程序分析:之所以實參數(shù)組a的各元素值會隨著形參數(shù)組array各元素值的改變而改變,主要是因為數(shù)組名代表的是數(shù)組的首地址,因此如果使用數(shù)組名作為實參,在調用函數(shù)時就會把數(shù)組的首地址傳給形參,這樣形參數(shù)組array和實參數(shù)組a就會共享同一段內存空間,所以當數(shù)組array中的元素值發(fā)生改變時,數(shù)組a中的各元素值也會隨之改變。如果想通過函數(shù)調用來改變實參數(shù)組中的元素值,除了可以使形參和實參都采用數(shù)組名之外,還可以使用以下三種方法:1、函數(shù)實參用數(shù)組名,形參用指針變量2、函數(shù)實參用指針變量,形參用數(shù)組3、函數(shù)實參、形參均用指針變量[演示][演示][演示]9.3.4二維數(shù)組的指針和指向二維數(shù)組的指針變量1、二維數(shù)組的指針二維數(shù)組的指針(地址)相比一維數(shù)組的指針有著很大的不同。在C語言中,系統(tǒng)是將二維數(shù)組當作一種特殊的一維數(shù)組來進行處理的。例如: inta[2][3]={{1,2,3},{4,5,6}};這是一個兩行三列的二維數(shù)組,C語言將該數(shù)組的每一行看作是一個一維數(shù)組,用數(shù)組名a[0]、a[1]來表示。這兩個一維數(shù)組分別又含有三個元素(每一行的三個列元素),即第一行數(shù)組a[0]含有:a[0][0]、a[0][1]、a[0][2];第二行數(shù)組a[1]含有:a[1][0]、a[1][1]、a[1][2]。這樣這兩行一維數(shù)組就構成了一個二維數(shù)組。如圖9-10所示。※注意:這里的a[0]、a[1]并不是實際存在的數(shù)組元素,它們代表的是每一行的數(shù)組名,而C語言又規(guī)定數(shù)組名代表數(shù)組首元素的地址,因此a[0]、a[1]分別代表第0行和第1行數(shù)組元素的首地址,即&a[0][0]和&a[0][1]除了可以使用每一行的一維數(shù)組名來代表該行的首地址之外,還可以使用二維數(shù)組名加下標的方法來表示各行元素的首地址。例如,二維數(shù)組名a代表第0行元素的首地址,那么a+1就代表第1行元素的首地址,a+2代表第2行元素的首地址,a+n代表第n行元素的首地址。同時,C語言又規(guī)定二維數(shù)組的行數(shù)組名a[0]與﹡(a+0)等價,a[1]與﹡(a+1)等價,因此二維數(shù)組的第i行元素的首地址還可以用﹡(a+i)來表示?!⒁猓河捎赼+i本身只是一個地址而不是一個實際的指針變量,所以﹡(a+i)并不代表指向a+i單元的值。請牢記,這里的﹡(a+i)只與一維數(shù)組名a[i]等價,只代表二維數(shù)組a的第i行元素的首地址。下面我們討論如何表示每行數(shù)組中各列元素的地址。以二維數(shù)組a為例,由于已知第0行元素的首地址為a[0],因此可以用a[0]+1表示第0行第1列元素的地址,對于第i行第j列元素的地址,可以用a[i]+j進行表示。又因為第i行數(shù)組元素的首地址a[i]與﹡(a+i)等價,所以第i行第j列元素的地址也可以用﹡(a+i)+j表示。表9-1顯示了二維數(shù)組各元素的地址、值的表示形式。例9-17采用不同的地址計算方法輸出二維數(shù)組元素/*exam9-17*/#include“stdio.h”main(){inti,j;inta[2][3]={{1,2,3},{4,5,6}};for(i=0;i<2;i++){ for(j=0;j<3;j++){ printf(“%d”,a[i][j]);//使用數(shù)組下標法輸出數(shù)組元素printf(“%d”,﹡(a[i]+j));//使用地址法輸出數(shù)組元素 printf(“%d”,﹡(﹡(a+i)+j));//使用地址法輸出數(shù)組元素} printf(“\n”);}}[演示]2、指向二維數(shù)組的指針變量使用指向二維數(shù)組的指針變量既可以順序訪問二維數(shù)組的元素值也可以訪問某個指定二維數(shù)組的元素值。(1)使用指針變量順序訪問二維數(shù)組的元素值。先看一個例子:例9-18使用指針變量順序輸出二維數(shù)組元素/*exam9-18*/#include“stdio.h”main(){inta[2][3]={{1,2,3},{4,5,6}};int﹡p;for(p=a[0];p<a[0]+6;p++) printf(“%d”,﹡p);}123456程序運行結果為:在上例中,指針變量p首先指向二維數(shù)組a的第0行0列元素,然后通過p++使指針變量依次移動到下一個元素,從而實現(xiàn)對二維數(shù)組所有元素的順序訪問。(2)使用指針變量訪問某個指定的二維數(shù)組元素程序例9-18的方法雖然可以順序訪問二維數(shù)組的元素,但無法訪問某個指定的元素(例如a[2][1])。為了解決這個問題,C語言提供了一種可以指向一維數(shù)組的指針變量(二維數(shù)組就是由若干行一維數(shù)組構成的),使用這種指針變量可以方便地訪問二維數(shù)組中的任意一個元素,該指針變量也稱為二維數(shù)組的行指針變量。以下是行指針變量的定義形式:類型(﹡指針變量名)[n]說明:(1)()不能省略,如果省略了,指針變量就不再指向一維數(shù)組,而變成了一個指針數(shù)組。(2)n代表指向的一維數(shù)組所包含的元素數(shù)。例如,int(﹡p)[4]代表指針變量p指向一個含有4個元素的一維數(shù)組。(3)假設一個行指針變量p指向一個二維數(shù)組a,那么可以使用﹡(﹡(p+i)+j)來訪問a的第i行第j列元素。(因為p指向a的第0行,﹡(p+i)就代表a的第i行數(shù)組的首地址,﹡(p+i)+j代表a的第i行第j列元素的地址,因此﹡(﹡(p+i)+j)就代表數(shù)組a的第i行第j列元素的值。)例9-19使用行指針變量輸出二維數(shù)組的任意元素/*exam9-19*/#include“stdio.h”main(){inta[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};inti,j;int(﹡)p[4];scanf(“%d,%d”,&i,&j);//輸入要輸出的元素所在的行數(shù)和列數(shù)
p=a;
//p指向數(shù)組a的第0行printf(“a[%d][%d]=%d”,i,j,﹡(﹡(p+i)+j));//輸出第i行第j列元素的值}2,3↙a[2][3]=12程序運行結果為:[Return]9.4 字符串指針9.4.1字符串的指針表示和引用 6.3節(jié)介紹過,一個非空的一維字符數(shù)組實際上是一個字符串,因此我們可以定義一個字符型的指針變量指向該字符數(shù)組,從而可以通過該指針變量對字符串進行引用。例如:
例9-20利用指針變量輸出字符數(shù)組的內容/*exam9-20*/#include“stdio.h”main(){charstring[]=“Iamaboy”;char﹡str;str=string;/*指針變量str指向字符數(shù)組的首元素*/for(;﹡str!=’\0’;str++)/*逐個輸出字符數(shù)組中的各個字符*/ printf(“%c”,﹡str);str=string;/*指針變量str指向string的首地址*/printf(“\n”);printf(“%s”,str);/*將字符數(shù)組內容一次性輸出*/}IamaboyIamaboy程序運行結果為:從例9-20可以看出,當指針變量str指向字符數(shù)組string時(見圖9-11),若要輸出字符數(shù)組的內容可以采用兩種方法:一種是通過指針變量的移動逐個輸出字符數(shù)組的各字符;另一種方法是使用格式控制符“%s”將字符串內容一次性輸出。
※注意:(1)如要逐個輸出字符串中的字符時,printf函數(shù)的格式控制符使用“%c”。(2)如要一次性輸出整個字符串時,printf函數(shù)的格式控制符使用“%s”,輸出項使用指針變量名。(實際上系統(tǒng)首先輸出指針變量所指向的字符,然后使指針變量逐步后移,依次輸出剩余的字符,直至遇到字符串的結束標志’\0’為止)。(3)字符串在輸出時,不包括結束字符’\0’。
字符型指針變量除了可以指向一維字符數(shù)組外,還可以直接指向一個字符串。例如:
char﹡str=”Iamaboy”; printf(“%s”,str);程序輸出的結果是”Iamaboy”。在這里雖然沒有定義字符數(shù)組,但C語言實際在內存中就是使用字符數(shù)組來存放字符串的,因此: char﹡str=”Iamaboy”;等價于:char﹡str;str=”Iamaboy”;該程序是將”Iamaboy”的首地址賦給指針變量str,然后通過格式控制符”%s”將字符串的內容一次性輸出。※注意:由于a+i本身只是一個地址而不是一個實際的指針變量,所以﹡(a+i)并不代表指向a+i單元的值。請牢記,這里的﹡(a+i)只與一維數(shù)組名a[i]等價,只代表二維數(shù)組a的第i行元素的首地址。例9-21利用指針變量將字符串中的大寫字母轉換成小寫字母/*exam9-21*/#include“stdio.h”main(){charstring[];char﹡str;scanf(“%s”,string);/*輸入字符串內容*/str=string;for(;﹡str!=’\0’;str++)/*逐個輸出字符數(shù)組中的各個字符*/{if(﹡str>=’A’&&﹡str<=’Z’)﹡str=﹡str+32;/*將字符串中的大寫字母轉換為小寫字母*/}str=string;/*指針變量str重新指向string的首地址*/printf(“%s”,str);}aBcDEF↙abcdef程序運行結果為:9.4.2字符串指針作函數(shù)參數(shù)
和其他指針一樣,字符串指針也可以作為函數(shù)參數(shù),可以通過地址傳遞的方法將字符串從一個函數(shù)傳遞到另一個函數(shù)。在函數(shù)的調用過程中如果被調函數(shù)改變了字符串的內容,那么主調函數(shù)中字符串內容也會相應發(fā)生改變。如果函數(shù)形參為字符指針變量,那么主調函數(shù)可以使用數(shù)組名或指針變量名作為函數(shù)的實參:(1)函數(shù)形參使用指針變量,實參使用一維數(shù)組名。(2)函數(shù)形參實參均使用字符指針變量。[例9-22演示][例9-23演示][Return]9.5函數(shù)指針9.5.1指向函數(shù)的指針變量
由于函數(shù)本身也有地址,所以指針變量除了可以指向變量、字符串和數(shù)組外,還可以指向函數(shù)。函數(shù)是一組指令的集合,系統(tǒng)在編譯函數(shù)時會將這些指令裝入內存,C語言規(guī)定這些指令的首地址稱為函數(shù)的入口地址,也稱為函數(shù)的指針。因此,可以定義一個指針變量來存放某一個函數(shù)的指針,從而達到間接調用該函數(shù)的目的,這種指針變量也稱為指向函數(shù)的指針變量。定義形式為:數(shù)據(jù)類型(﹡變量名)(參數(shù)類型列表)說明:(1)“數(shù)據(jù)類型”指的是指針變量所指函數(shù)的返回值類型。(2)(﹡變量名)的()不能省略。例如:(﹡p)()不能寫成﹡p(),﹡p()代表的是一個返回指針類型的函數(shù)。(3)參數(shù)類型列表指的是定義指針變量時,指針變量的參數(shù)類型表必須和所指函數(shù)的形參列表一致,即形參的個數(shù)相同、類型相同。例如,有函數(shù)intfunction(inta,intb),在定義指向該函數(shù)的指針變量時,應當定義為:int(﹡p)(int,int)。(4)C語言規(guī)定函數(shù)名就代表函數(shù)的入口地址,因此給函數(shù)指針變量賦值時,只要將函數(shù)名賦給指針變量就可以了,而不必給出函數(shù)的參數(shù)。例如:intfuction(inta,intb){………}main(){int(﹡p)(int,int);/*定義指向函數(shù)的指針變量*/
p=function;
/*指針變量p指向函數(shù)function*/}9.5.2用函數(shù)指針變量調用函數(shù)當一個函數(shù)指針變量指向一個函數(shù)后,就可以使用這個指針變量來間接調用函數(shù)。下面先看一個普通函數(shù)調用的例子。
例9-25編寫函數(shù)求兩個整型數(shù)的和/*exam9-25*/#include“stdio.h”intadd(inta,intb){intc;c=a+b;return(c);}main(){intx,y,z;scanf(“%d,%d”,&x,&y);/*輸入兩個整數(shù)*/z=add(x,y);printf(“%d+%d=%d”,x,y,z);}本程序中,通過將x,y的值作為實參傳遞給add函數(shù),從而完成對add函數(shù)的調用,這也是一般調用函數(shù)的方法。如果使用函數(shù)指針變量調用add函數(shù),只需在函數(shù)調用時將函數(shù)名add改為(﹡變量名)即可。例如,將main函數(shù)改為: int(﹡p)(int,int); p=add;
z=(﹡p)(x,y);因此,使用指針變量調用函數(shù)時,除了使用(﹡變量名)代替函數(shù)名外,還應該根據(jù)需要加上函數(shù)的實參。另外需要注意的是,由于函數(shù)指針變量只能指向函數(shù)的入口地址而不能指向其他的任何地方,因此在使用函數(shù)指針變量時,不能夠做類似于p++或p--的操作。例9-26使用函數(shù)指針變量調用函數(shù)找出任意兩個數(shù)中的小數(shù)/*exam9-26*/#include“stdio.h”intmin(intm,intn){ints;if(m<n)s=m;elses=n;return(s);}main(){inta,b,c;int(﹡p)(int,int);printf(“Inputthenumber:\n”);scanf(“%d,%d”,&a,&b);p=min;c=(﹡p)(a,b);printf(“Theminimumnumberis:\n%d”,c);}Inputthenumber:9,6↙Theminimumnumberis:6程序運行結果:9.5.3用指向函數(shù)的指針作函數(shù)參數(shù) 指向函數(shù)的指針最常見的用途就是將該指針作為函數(shù)的參數(shù)傳遞給被調函數(shù),通過函數(shù)地址值的傳遞,實現(xiàn)被調函數(shù)可以根據(jù)不同的情況來調用不同的函數(shù),從而增強程序的靈活性。其一般的使用方法為:假設有兩個普通函數(shù):intfun1(intx1,inty1)、intfun2(intx2),現(xiàn)定義一個形參為函數(shù)指針的函數(shù)來完成對fun1和fun2的調用。例如:
intfun1(intx1,inty1){ ……}intfun2(intx2){ ……}fun(int(﹡p1)(int,int),int(﹡p2)(int)){ inta,b;
a=(﹡p1)(5,6);
}main(){
fun(fun1,fun2);}在main()函數(shù)中,程序將函數(shù)fun1和fun2的入口地址傳給了函數(shù)fun的兩個參數(shù)p1和p2,這樣p1、p2就分別指向了函數(shù)fun1和fun2(見圖9-13),這也意味著就可以使用函數(shù)指針p1和p2間接訪問函數(shù)fun1和fun2了。在這里需要注意的是,如果main函數(shù)放在了函數(shù)fun1和fun2的上面,那么在main函數(shù)調用fun(fun1,fun2)之前需要對實參函數(shù)fun1和fun2進行聲明,即:
main() { intfun1(int,int); intfun2(int); fun(fun1,fun2); }因此,由上可以看出使用含有函數(shù)指針參數(shù)的函數(shù)最大的好處就是可以靈活地調用函數(shù),特別是當經(jīng)常調用的函數(shù)是不固定的時候。例如,假設程序第一次需要調用fun1和fun2函數(shù),第二次需要調用fun3和fun4函數(shù),這時如果使用傳統(tǒng)的直接調用函數(shù)的方法就會需要每次都得修改fun函數(shù),這樣就顯得十分繁瑣。但是如果使用函數(shù)指針變量來調用這些函數(shù),那么每次只要賦給fun函數(shù)不同的實參值就可以了,而不必修改fun函數(shù)本身。[Return]9.6 返回指針值的函數(shù)函數(shù)的返回值可以有多種類型,除了此前學習的整型、實型和字符型以外,函數(shù)還可以返回指針型數(shù)據(jù),即返回的數(shù)據(jù)是一個地址。返回值為指針類型的函數(shù)的定義形式一般如下:
類型﹡函數(shù)名(形參列表)
{
…… }說明:(1)類型指的是返回的指針指向的是什么類型的數(shù)據(jù)。(2)函數(shù)名前的﹡代表的是該函數(shù)返回值是一個指針數(shù)據(jù),即調用該函數(shù)的程序可以得到一個地址。例如: int﹡fun(inta) {
…… }代表定義了一個函數(shù),該函數(shù)的返回值是一個指向整型數(shù)據(jù)的地址。
[例9-28]根據(jù)例9-28可以看出,main函數(shù)在調用fun函數(shù)的時候,首先將字符串string的首地址賦給fun函數(shù)的形參﹡str,然后通過對str的操作將字符串中的大寫字母轉換成小寫字母,最后將轉換好的字符串首地址返回給主調函數(shù)main,因此最后輸出的s所指的數(shù)據(jù)就是已經(jīng)轉換好的字符串string。【思考】在fun函數(shù)中是否有必要定義指向形參str的字符型指針變量p?[例9-29]※注意:函數(shù)返回的指針不能指向函數(shù)返回后便不存在的變量。例如: int﹡f(inta) { return&a; }這個函數(shù)返回的結果是非法的,主要因為變量a為形參變量,該變量的生存周期是函數(shù)f的運行階段,即當f執(zhí)行完后,形參變量a也會隨之被釋放,因此主調函數(shù)所獲得的返回值是不可靠的。9.7 指針數(shù)組和指向指針的指針9.7.1指針數(shù)組 普通數(shù)組元素除了可以存儲整型、實型和字符型數(shù)據(jù)外,還可以存放指針型數(shù)據(jù)。如果某種數(shù)組存放的所有元素都是指向同一類型的指針變量,則稱該數(shù)組為指針數(shù)組。指針數(shù)組的一般定義形式為:
類型標識符﹡數(shù)組名[長度]例如:
int﹡p[3];該語句定義了一個長度為3的指針數(shù)組,每個指針元素都可以指向整型變量。除此之外,C語言還允許在定義指針數(shù)組的同時就可以完成對數(shù)組的初始化操作。例如:
char﹡str[3]={“cat”,“dog”,“pig”};該語句定義了指針數(shù)組str,并且數(shù)組的3個元素分別指向3個字符串常量,即str的三個元素分別存放了三個字符串(字符串在內存中的存儲形式是一維字符數(shù)組)的首地址。如圖9-14。※注意:在定義指針數(shù)組時,一定不能將int﹡p[3]寫成int(﹡p)[3],因為int(﹡p)[3]代表的是指向一維數(shù)組的指針變量。
指針數(shù)組最常被用于處理多個字符串,雖然使用普通二維字符數(shù)組也可以存放和管理多個字符串(由于每個字符串都是一個一維字符數(shù)組,因此需要使用二維數(shù)組來存儲多個字符串),但是在效率方面要比使用指針數(shù)組低的多。主要理由如下:(1)使用二維數(shù)組存放多個字符串時,由于二維護數(shù)組在定義時列數(shù)就是固定的,即每一行數(shù)組的元素個數(shù)相同,因此往往使用最長長度來定義二維數(shù)組的列數(shù)。而實際上每個字符串的長度可能是不等的,這樣就會造成內存空間的浪費,例如:charstr[3][9]={“dog”,“duck”,“elephant”}在內存中的存儲形式如圖9-15所示;而字符指針數(shù)組的各個指針元素只是指向各個一維字符數(shù)組,指針數(shù)組的長度與字符串的長度無關,因此不存在內存浪費的問題。(2)使用二維數(shù)組來處理多個字符串時,特別當涉及字符串的交換、復制和排序等操作時,需要將字符串整個進行移動;而使用字符指針數(shù)組來處理這些操作時,不必改變各字符串在內存中的位置,只需改動指針數(shù)組中各元素的指向就可以了。因此使用字符指針數(shù)組來處理多個字符串的效率要比二維字符數(shù)組高得多。例9-30編寫程序用來找出4個字符串中按字母排序最大的字符串/*exam9-30*/#include“stdio.h”#include“string.h”main(){ char﹡str[4]={“bee”,“dog”,“pig”,“elephant”}; inti,j,k; char﹡s; for(i=0;i<3;i++) { k=i; for(j=i+1;j<4;j++) { if(strcmp(str[k],str[j])>0)k=j; } if(k!
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 【正版授權】 ISO 4070:2025 EN Polyvinylidene fluoride (PVDF) - Effect of time and temperature on expected strength
- 金屬餐具的供應鏈管理優(yōu)化考核試卷
- 紡織行業(yè)的經(jīng)濟價值考核試卷
- 計算機網(wǎng)絡設計與實施相關試題及答案
- 公路施工決策分析試題及答案
- 數(shù)據(jù)庫安全策略與用戶管理試題及答案
- 鉆探設備在寶石礦勘查中的技術要求考核試卷
- 液體乳品物流與供應鏈優(yōu)化策略考核試卷
- 計算機三級考試中心知識回顧與試題及答案
- 計算機在多媒體信息處理與內容分發(fā)考核試卷
- 國家開放大學《中文學科論文寫作》形考任務1-4參考答案
- 【真題】2023年常州市中考道德與法治試卷(含答案解析)
- 酒吧計劃創(chuàng)業(yè)計劃書
- 光伏項目安全培訓課件
- 拉森鋼板樁監(jiān)理實施細則樣本
- 個人房屋抵押借款合同范本-借款合同
- 《原碼一位乘法》課件
- 中華人民共和國監(jiān)察法學習解讀課件
- 中小學教務主任培訓
- 眼鏡行業(yè)目標市場分析
- 空間向量與立體幾何教材分析
評論
0/150
提交評論