版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
第7章指針、函數和數組7.1變量的地址和指針7.2指針變量7.3一維數組和指針7.4一維數組和函數7.5二維數組和指針7.6二維數組和函數7.7字符串和指針7.8指向函數的指針7.9返回指針值的函數7.10指針數組和二級指針7.1變量的地址和指針存儲空間:存儲空間(StorageSpace)是一種邏輯存儲結構,它對上層應用提供尋址空間和相應的邏輯存儲單元(LogicStorageUnit),并提供操作集(AccessOperations)用于存取存儲單元中的數據;同時在內部具有表示存儲空間的數據結構(Metadata)實現(xiàn)邏輯存儲單元和下層存儲空間地址的映射關系;具有訪問下層存儲空間的存取操作集,把上層對于邏輯存儲單元的數據存取請求解析傳遞給下級存儲空間。C語言程序任何一個變量都必須占用一定的存儲空間。變量的存儲空間依變量的類型而決定。7.1變量的地址和指針例7.1不同類型變量的存儲空間#include<stdio.h>main(){
int
i;long
l;char
c;floatf; doubled;
printf("int變量占用的存儲空間為:%d\n",sizeof(i));
printf("long變量占用的存儲空間為:%d\n",sizeof(l));
printf("char變量占用的存儲空間為:%d\n",sizeof(c));
printf("float變量占用的存儲空間為:%d\n",sizeof(f));
printf("double變量占用的存儲空間為:%d\n",sizeof(d));
printf("\n");}運行結果:int變量占用的存儲空間為:4long變量占用的存儲空間為:4char變量占用的存儲空間為:1float變量占用的存儲空間為:4double變量占用的存儲空間為:8分析:int型變量的存儲空間為4。long型變量的存儲空間為4,char型變量的存儲空間為1,float型變量的存儲空間為4,double型變量的存儲空間為8。其中,單位為字節(jié)。7.1變量的地址和指針問題:變量的存儲空間的實際地址應該如何獲???C語言中,我們采取&變量名的方法完成變量地址的獲取。采用&變量名的方法取得變量的地址也是一個值,該內存地址值就是實際占用多少存儲空間7.1變量的地址和指針例7.2變量地址的存儲空間#include<stdio.h>main(){
int
i;long
l;char
c;floatf; doubled;
printf("int變量占用的存儲空間為:%d\n",sizeof(&i));
printf("long變量占用的存儲空間為:%d\n",sizeof(&l));
printf("char變量占用的存儲空間為:%d\n",sizeof(&c));
printf("float變量占用的存儲空間為:%d\n",sizeof(&f));
printf("double變量占用的存儲空間為:%d\n",sizeof(&d));
printf("\n");}運行結果:int變量占用的存儲空間為:4long變量占用的存儲空間為:4char變量占用的存儲空間為:4float變量占用的存儲空間為:4double變量占用的存儲空間為:4分析:從上面的例子可以看到,基本數據類型變量在系統(tǒng)中使用4個字節(jié)來表示該變量的內存地址。那么考慮是否有有一種方式:直接通過地址來訪問該變量,而無需使用該變量的名字?答案是肯定的,那就是指針變量。而變量的內存地址通常被稱為:指針。7.2指針變量7.2.1指針變量的定義7.2.2怎樣引用指針變量7.2.3指針變量作為函數參數7.2.1指針變量的定義指針:就是某個變量的內存地址。如果有一種特殊變量能夠存儲各種類型變量的內存地址,則在使用當中通過改變這種特殊變量的值就可以達到變換不同內存地址的目的。那么當然有可能通過該特殊變量來訪問對應的內存。指針變量:定義為存儲變量內存地址的一種特殊變量。7.2.1指針變量的定義指針變量的聲明方法比較簡單,且任何一種類型的變量都可以聲明指針變量。格式:類型*指針變量名;例7.3指針的聲明int *p; //p被聲明為整型變量的指針
char *pc; //pc被聲明為字符變量的指針
long *pl; //pl被聲明為長整型變量的指針
float *pf; //pf被聲明為浮點型變量的指針
double *pd; //pd被聲明為雙精度浮點類型變量的指針//也可以聲明為無類型的指針:void *pv; //pv被聲明為任意類型變量的指針7.2.1指針變量的定義既然指針是某個內存的地址,指針變量是一種特殊變量,指針的示意圖可以使用右邊的圖來表達前面的例子:返回7.2.2怎樣引用指針變量指針是一種特殊類型的變量,任何一種類型的變量都可以把其所在的內存地址賦給指針變量。賦值的方法如下:指針變量名=&變量名;7.2.2怎樣引用指針變量例7.4指針類型的賦值與占用的存儲空間#include<stdio.h>main(){
inti,*pi;longl,*pl;charc,*pc;floatf,*pf;doubled,*pd; pi=&i;pl=&l;pc=&c;pf=&f; pd=&d;
printf("int變量的指針占用的存儲空間為:%d\n",sizeof(pi));
printf("long變量的指針占用的存儲空間為:%d\n",sizeof(pl));
printf("char變量的指針占用的存儲空間為:%d\n",sizeof(pc));
printf("float變量的指針占用的存儲空間為:%d\n",sizeof(pf));
printf("double變量的指針占用的存儲空間為:%d\n",sizeof(pd));
printf("\n");}運行結果:int變量的指針占用的存儲空間為:4long變量的指針占用的存儲空間為:4char變量的指針占用的存儲空間為:4float變量的指針占用的存儲空間為:4double變量的指針占用的存儲空間為:4分析:上例子可見,賦值給指針變量的方法很簡單,只需要取得變量的地址就可以把該地址賦值給對應類型的指針變量了。7.2.2怎樣引用指針變量問題:那么如何使用該指針變量?使用指針變量的目的在于使用另外一條途徑對變量進行操作。指針變量的引用方法如下:格式:*指針名=表達式;請?zhí)貏e注意:指針名=&變量名;與*指針名=表達式;兩者之間的區(qū)別:1、指針名=&變量名;//這個語句表達了:把變量的地址存放到指針變量中。2、*指針名=表達式;//這個語句表達了:將表達式的計算結果值存儲到指針變量所存放的內存地址所代表的內存單元中去。7.2.2怎樣引用指針變量例7.5引用指針變量的例子#include<stdio.h>main(){
inti,*pi=&i; longl,*pl=&l; charc,*pc=&c; floatf,*pf=&f; doubled,*pd=&d; i=1;
printf("i變量的值為:%d\n",i); *pi=10;
printf("i變量的值改變?yōu)椋?d,*pi的值為:%d\n",i,*pi);
l=16;
printf("l變量的值為:%ld\n",l); *pl=10;
printf("l變量的值改變?yōu)椋?d,*pl的值為:%ld\n",i,*pl);
c='a';
printf("c變量的值為:%c\n",c); *pc='D';
printf("c變量的值改變?yōu)椋?c,*pc的值為:%c\n",c,*pc);
f=1.5;
printf("f變量的值為:%f\n",f); *pf=55.5;
printf("f變量的值改變?yōu)椋?f,*pf的值為:%f\n",f,*pf); d=123.456;
printf("d變量的值為:%lf\n",d); *pd=654.321;
printf("d變量的值改變?yōu)椋?lf,*pd的值為:%lf\n",f,*pd);
printf("\n");}運行結果:i變量的值為:1i變量的值改變?yōu)椋?0,*pi的值為:10l變量的值為:16l變量的值改變?yōu)椋?0,*pl的值為:10c變量的值為:ac變量的值改變?yōu)椋篋,*pc的值為:Df變量的值為:1.500000f變量的值改變?yōu)椋?5.500000,*pf的值為:55.500000d變量的值為:123.456000d變量的值改變?yōu)椋?5.500000,*pd的值為:654.321000分析:例子清楚地說明了指針變量是變量的別名,通過指針變量獲得了變量的另外一種操作方式,給變量的賦值提供了另一種操作方法。但是,應該注意到,由于優(yōu)先級與結合性等問題,指針的合理使用非常關鍵,看這樣一個例子7.2.2怎樣引用指針變量例7.6若定義語句:intyear=2009,*p=&year;,以下不能使變量year中的值增至2010的語句是:(2011年9月全國計算機等級考試二級C試題選擇題第25題)A.*p+=1;B.(*p)++;C.++(*p);D.*p++;分析:答案A.*p+=1;可理解為:*p=*p+1,即2009+1=2010。 答案B.(*p)++;語句同A答案只是明確了優(yōu)先級為(*p)的內容自加一 答案C.++(*p);同答案B
答案D.*p++;由于++的優(yōu)先級高于*故此先做加法,即先把p的值增加一,這樣做的結果是:指針變量p中保存的year變量地址被改變了(p變量的內容加一之后,保存的內容就已經不是year變量的地址了,具體是什么地址是不確定的),則*p的內容也就無法預知。返回7.2.3指針變量作為函數參數指針變量具有高度的靈活性,它可以作為函數的參數來使用。將指針作為函數的參數使用方法如下:函數返回值函數名(…,類型名*指針名,…)也就是簡單地將指針(地址)賦給函數作為函數的參數即可。特別需要注意的是:當函數的形參為指針,若實參為變量時提供的是變量的地址,需要使用:&變量名。同樣,當函數的形參為指針,若實參為數組時,提供的是數組名(數組名就是數組的首地址),即:數組名。關于數組名就是數組的首地址問題在下一節(jié)中會詳細介紹。這里首先來看指針變量做為函數的參數。7.2.3指針變量作為函數參數例7.7指針作為參數傳遞(2010年3月全國計算機等級考試二級C試題選擇題第26題)#include<stdio.h>//定義了函數fun。其中參數c是一個指針變量,而d不是指針變量voidfun(char*c,intd){ *c=*c+1; d=d+1;
printf("%c,%c",*c,d);}main(){ charb='a',a='A';
fun(&b,a);
printf("%c,%c\n",b,a);}程序運行后的輸出結果是
A)b,B,b,A
B)b,B,B,A
C)a,B,B,a
D)a,B,a,B7.3一維數組和指針7.3.1數組首地址7.3.2數組元素的指針7.3.3通過指針引用數組元素7.3.1數組首地址回顧:數組名就是數組的首地址。因指針變量存儲的內容就是一個地址,故理論上就可以直接把數組名賦值給指針變量。由于C語言的編譯系統(tǒng)對于數組中某個元素的表示與指向數組的指針表示某個數組元素的時候是同等對待的,因此對于指向數組的指針是對數組操作的另外一種表現(xiàn)形式。7.3.1數組首地址首先對比變量的地址賦值給指針變量與數組名賦值給指針變量。int i,*p=&i; //變量使用取地址“&”符號來取得其地址值,賦值給指針int a[10],*p=a; //數組直接將其名字賦值給指針可以看到,數組的首地址是可以賦值給指針變量的。數組的聲明方式通常為: 類型名 數組名[長度];上述的聲明實際上產生了如下的一種內存結構:
上圖可以清晰地了解到,數組中任何一個元素的引用方式都是使用:數組名[下標編號]的方式來引用的。數組名作為整個數組的標示,表達為某類型整個數組空間的首地址,以便于使用下標引用方式逐一訪問返回7.3.2數組元素的指針方式一:普通方式可以使用如下方式來給指針賦值,使得該指針指向數組中的某個元素:例7.8給數組元素賦值的普通方式inta[10],*p; ……p=&a[5]; ……p=&a[0]; ……分析:語句inta[10],*p;聲明了一個數組a,它有十個整型元素。并聲明了一個整數指針變量。語句p=&a[5];如果希望訪問數組a的第六個元素,則給他制造一個新名字:*p,也就是將數組的第六個元素的地址(&a[5])直接賦值給整型指針變量p。語句p=&a[0];如果希望訪問數組a的第一個元素,可以使用上述的辦法。這個用法完全等價于p=a,這是因為第一個元素的地址就是整個數組的首地址7.3.2數組元素的指針方式二:遞加方式假設對指針的定義如下:inta[10],*p=a; 則a數組的任意一個元素可以使用下面的方式來表達:數組a中的第i個元素的名稱是:a[i],也可以是:*(p+i)。這種方式提供了更加靈活的數組訪問方法。7.3.2數組元素的指針上述兩種用法可以通過一張圖來對比:返回7.3.3通過指針引用數組元素方法一:普通引用法。當對數組的某個元素直接訪問時,可以使用這種方法。例7.9數組元素的普通引用inta[20],*p;
p=&a[5]; //取得第六個元素的地址*p=34; //給第六個元素賦值為整數34分析:上述代碼的功能為訪問數組的第六個元素。語句p=&a[5];的作用是取得第六個元素的地址。語句*p=34;是給第六個元素賦值為整數34。很明顯,上述的方法只能給數組中的某一個元素賦值,如果希望利用指針給數組中所有元素賦值,則可以如下編寫代碼:例7.10使用指針給數組全部元素賦值為1inta[20],*p;inti;
p=a; for(i=0;i<20;i++,p++)*p=1;分析:注意到,上述代碼中語句p=a;與語句p=&a[0];等價。該語句的作用為取得數組的首地址。7.3.3通過指針引用數組元素方法二:遞加引用。當引用數組元素的時候也可以采用固定首地址,而改變其增量來實現(xiàn)普通方法的賦值方式。例7.11數組某一個元素的遞加引用inta[20],*p;inti;
p=a; *(p+5)=34; 分析:上述代碼的功能為訪問數組的第六個元素。語句p=a;與語句p=&a[0];等價。該語句的作用為取得數組的首地址。語句*(p+5)=34;作用為給第六個元素賦值為整數34。例7.12使用指針給數組全部元素賦值為1inta[20],*p;inti;
p=a; //語句p=&a[0];與p=a;等價。該語句的作用為取得數組的首地址。for(i=0;i<20;i++,p++)*(p+i)=1;分析:語句p=a;與語句p=&a[0];等價。該語句的作用為取得數組的首地址。7.4一維數組和函數7.4.1數組元素做函數參數7.4.2數組名做函數參數7.4.3數組元素地址做函數參數7.4.4函數的指針形參和函數體中數組的區(qū)別7.4.1數組元素做函數參數在函數的參數當中,可以使用普通變量(自動變量)。這種用法與使用數組的元素作為參數參數的用法完全一致。即:例7.13函數參數使用普通變量與數組元素作為函數參數int i=5,a[3]={5,5,5};fun(i);或者使用fun(a[2]);分析:上述兩個語句實現(xiàn)了完全相同的功能。就是將值為5的變量傳遞給函數fun,從傳遞的參數值、傳遞方式、傳遞后的處理機制來看,fun(i)與fun(a[2])沒有本質上的區(qū)別。這是因為希望傳遞的參數值為5,變量i與數組的三個元素的值都是5;第二,參數傳遞時都沒有用到&符號來取得地址;第三,都只使用了變量本身。這說明了一個問題:數組中任意一個元素在實質上就是一個普通變量。因此如果不考慮變量名的情況下,上例中的兩個語句完全等價。7.4.1數組元素做函數參數還應該特別看到:在調用函數fun時,調用的機制也是完全一樣的。即進入fun函數時,產生變量的副本(數組元素作為變量來產生副本),然后進入函數、完成函數功能、退出函數時將產生的副本刪除,其存儲空間交還給系統(tǒng)。例7.13中fun(i)與fun(a[2])的調用流程如下圖:返回7.4.2數組名做函數參數數組的中元素作為函數參數的傳遞過程中傳遞的是該元素的值。在這個傳遞過程中,進入函數后為了保證該值不能被改變,故函數為該參數生成了一個副本。在函數退出時,刪除該副本的存儲空間。這樣參數的值既能被函數所使用,又能保證在函數執(zhí)行過程中參數的值不會變化。理解這個過程類似于理解我們數學上的函數運算:例7.14數學的運算關系
y=2x+6當x=1時,有:
y=2*1+6=8注意到,此時的x仍然等于1.那么很多時候需要改變數組中的元素值,則此時可以使用兩種方式來解決這個問題,第一種方式就是傳遞數組的首地址,也就是數組名作為函數的參數。7.4.2數組名做函數參數例7.15有下程序(函數fun只對下標為偶數的元素進行操作)。(2010年9月全國計算機等級考試二級C試題選擇題第30題)#include<stdio.h>voidfun(int*a,intn){
int
i,j,k,t; for(i=0;i<n-1;i+=2) { k=i;
for(j=i;j<n;j+=2)
if(a[j]>a[k])k=j; t=a[i];
a[i]=a[k];
a[k]=t; }}main(){
intaa[10]={1,2,3,4,5,6,7},i;
fun(aa,7); //調用fun函數
for(i=0;i<7;i++)printf("%d,",aa[i]); //打印執(zhí)行之后的數組值
printf("\n");}
程序運行后的輸出結果是:
A)7,2,5,4,3,6,1
B)1,6,3,4,5,2,7
C)7,6,5,4,3,2,1
D)1,7,3,5,6;2,1答案:選A。返回7.4.3數組元素地址做函數參數在某些時候僅僅只需要訪問數組中的一個元素,則通常無需傳遞首地址。僅僅只需傳遞該元素地址即可。例7.16訪問數組的某一個元素#include<stdio.h>voidfun(int*p){ *p=6;}main(){
inta[10]={1,2,3,4,5,6,7,8,9,10};
inti;
printf("\na數組原來的值為:\n"); for(i=0;i<10;i++)printf("%d",a[i]); fun(&a[2]);
printf("\na數組現(xiàn)在的值為:\n"); for(i=0;i<10;i++)printf("%d",a[i]);
printf("\n");}運行結果:a數組原來的值為:
12345678910a數組現(xiàn)在的值為:2645678910分析:上述程序的功能是將數組的第3個元素值打印出來,并將其原來值3改為6。7.4.3數組元素地址做函數參數特別注意到,即便是傳遞數組中的一個元素地址,也可以訪問數組中其他的元素。因為該元素地址已經幫指針定位到了數組的某一個元素的位置??梢酝ㄟ^這個位置推斷其他元素的位置。下面我們來看一個例子:例7.17通過元素訪問數組(2011年9月全國計算機等級考試二級C試題第27題)#include<stdio.h>voidfun(int*p){printf(“%d\n”,p[5]); }main(){inta[10]={1,2,3,4,5,6,7,8,9,10};fun(&a[3]); }程序運行后的輸出結果是A.5B.6C.8D.9答案:選D。返回7.4.4函數的指針形參和函數體中數組的區(qū)別為什么有區(qū)別?函數把指向數組的指針作為形參的時候和函數體中的數組是有本質區(qū)別的。當指向數組的指針作為函數的參數時,事實上對于實參是一個副本。因此將指向數組的指針參數傳遞給函數并不改變該參數的具體數值,也就是無法改變該參數。因為即使函數體中對該形參有賦值等操作,也是改變該形參的副本。但是對于函數體中的數組,是進入函數體是分配空間,函數執(zhí)行結束時被系統(tǒng)收回。因此兩者具有顯著的區(qū)別。7.4.4函數的指針形參和函數體中數組的區(qū)別區(qū)別一:將指向數組元素的指針作為函數的形參如7.17中語句fun(&a[3]);實際上傳遞給函數的實參是數組a的第四個元素的地址。p使用的僅僅是:數組a的第四個元素的地址。這里的&a[3]使用方法是:取得數組a第四個元素地址,并將其作為常量來賦值給函數fun的變量p來使用。在例7.15中語句fun(aa,7);使用的是數組的首地址aa(數組名aa),并將數組名值賦值給指針變量a,然后將指針作為數組來使用。事實上這也是可行的。但是注意到,這種使用方法僅僅是fun函數中的指針變量a與main函數中的數組aa建立了一個臨時聯(lián)系而已。在fun函數中通過這種臨時聯(lián)系來訪問main函數中的aa數組。當fun函數執(zhí)行完畢時,指針變量a的空間會交還給操作系統(tǒng)(注意到此時main函數尚未執(zhí)行完),這個聯(lián)系被切斷,但是并未將main函數中的aa數組空間交還給操作系統(tǒng)。將數組地址作為參數時,改變的是該地址對應的數組內容,當函數執(zhí)行完畢,修改會保存。7.4.4函數的指針形參和函數體中數組的區(qū)別區(qū)別二:函數體中的數組函數體中的數組與參數不同,函數體中數組的作用范圍僅僅限于本函數(聲明為靜態(tài)除外)。因此在函數執(zhí)行完畢之后,該數組空間將不復存在。下一次在調用此函數時,則重新生成一個該函數的調用副本,函數中的數組空間也重新分配。當然,執(zhí)行結束時,數組空間仍然會還給操作系統(tǒng)。也就是函數中的數組僅在本函數內部有效!7.5二維數組和指針7.5.1二維數組首地址7.5.2二維數組元素的地址7.5.3二維數組元素的指針變量7.5.4指向第一維數組的指針變量7.5.1二維數組首地址同一維數組一樣,二維數組的數組名就是二維數組的首地址。首地址表達了二維數組的基本訪問位置。C語言中,對于數組的實現(xiàn)而言,多維數組與一維數組是一樣的,也就是說在實現(xiàn)方法上只有一維數組。因此,其二維數組是作為多個一維數組來對待的。例7.18多維數組分解inta[3][5];則可以這樣解釋:a數組的第一維有3個元素。每個元素是一維數組,其中一維數組的元素個數是5個。如右圖:
返回7.5.2二維數組元素的地址可以看到,二維數組的關鍵問題在于如何表達和計算二維數組元素的地址。找到其地址的目的是訪問某個元素。當聲明或定義一個二維數組時,其某個元素的地址可以依照如下公式來計算:某元素的地址=首地址+某元素的行下標*行元素個數+列下標例7.19數組元素地址的計算inta[5][7];分析:上述定義了5行7列的二維數組(二維數組可以直接理解為一個行乘以列形式的方陣)。元素a[2][3]的地址=元素a[0][0]的地址+2*7+3=元素a[0][0]的地址+17注意到上面的地址公式計算中行元素個數為7個。這里我們計算的元素a[2][3]的地址是用來給指針變量用的,假設有個指針變量被賦值為a數組的首地址的話。實際上如果僅僅使用數組訪問的時候,我們是無需該地址的。因為a[2][3]這個寫法已經表達了該變量。另外一種方法就是直接取得數組元素a[2][3]的地址:&a[2][3]。返回7.5.3二維數組元素的指針變量討論:指針變量如何賦值、指針變量如何訪問二維數組中的某個元素。指針變量的賦值與一維數組完全一致,僅僅數組是二維而已。來看一個例子:例7.20用指針訪問二維數組中最后一個元素#include<stdio.h>main(){
inta[3][2]={{0,0},{0,0},{0,0}},*p; p=a[0]; *(p+2*2+1)=9;
printf("\n%d\n",a[2][1]);}運行結果:
97.5.3二維數組元素的指針變量使用指針訪問二維數組的步驟:第一步:定義指針變量與二維數組。例如:inta[3][2],*p;第二步:將數組的首地址賦值給指針變量。例如:p=&a[0][0]或者是p=a[0].第三步:使用指針來訪問二維數組的第i行與第j列的某個元素。a[i][j]對應*(p+行下標i*行元素個數+列下表j)7.5.3二維數組元素的指針變量例7.21使用一級指針訪問二維數組#include<stdio.h>main(){
inta[3][2]={{0,1},{10,11},{20,21}},*p;
int
i,j; p=a[0];
for(i=0;i<3;i++) for(j=0;j<2;j++)
printf("%d",*(p+i*2+j));
printf("\n");}運行結果:0110112021Pressanykeytocontinue返回7.5.4指向第一維數組的指針變量數組與指針的一個重要用法是:指向多維數組的指針。指向數組的指針說明如下:類型名(*指針名)[數組長度];解釋為:說明了一個指針,該指針指存儲了二維數組的第一維的地址,其第二維的元素個數為數組長度。比如:int
(*p)[3];解釋為:p是一個指針,該指針未來記錄一個二維數組的第一維的地址,其第二維的元素為int類型,第二維的元素個數為3。可見,事實上指向一個二維數組的第一維的指針(上面的指針說明形式)實際上就是:定義了一個存放二維數組的首地址的指針變量。7.5.4指向第一維數組的指針變量例7.22若有定義int(*pt)[3];,則下列說法正確的是(2010年3月全國計算機等級考試二級C試題選擇題第27題)
A)定義了基類型為int的三個指針變量
B)定義了基類型為int的具有三個元素的指針數組pt
C)定義了一個名為*pt、具有三個元素的整型數組
D)定義了一個名為pt的指針變量,它可以指向每行有三個整數元素的二維數組 分析:根據題意,pt實際上是一個整型指針,而且是一個指向指針的指針,它指向的是一個有3個元素的整型數組的數組的首地址,因此相對于二維數組的數組名。所以,選D。 可見,指針與二維數組名屬于同等級別,因此在賦值的時候只需要將數組名直接賦值給指針即可。7.5.4指向第一維數組的指針變量例7.23若有定義語句:chars[3][10],(*k)[3],*p;,則以下賦值語句正確的是(2011年3月全國計算機等級考試二級C試題選擇題第28題)
A)p=s;B)p=k;C)p=s[0];D)k=s;分析:選項A的含義是將二維數組的首地址賦值給了一個指針,選項B的含義是將一維數組的指針賦值給了一個指向變量的指針,選項C的含義是將一個一維數組首地址賦值給了一個指針,選項D的含義是將二維數組賦值給了指向一維數組的指針。選C。7.6二維數組和函數7.6.1二維數組名做函數參數7.6.2指向第一維數組的指針變量做函數參數7.6.1二維數組名做函數參數將二維數組名作為函數的參數來傳遞,關鍵在于函數的參數必須定義為指針類型或者是二維數組。此時,由于數組名代表了數組的首地址,則函數中對數組元素的訪問就改變了調用函數中數組元素的值。來看這樣一個例子:例7.24有以下程序(2011年9月全國計算機等級考試二級C試題選擇題第28題)#include<stdio.h>#defineN4voidfun(inta[][N],intb[]){
inti;
for(i=0;i<N;i++)b[i]=a[i][i]-a[i][N-1-i];}main(){intx[N][N]={{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}},y[N],i; fun(x,y);for(i=0;i<N;i++)printf("%d,",y[i]);printf("\n");}程序運行后的輸出結果是:A.-12,-3,0,0,B.-3,-1,1,3,C.,0,1,2,3,D.-3,3,-3,-3,7.6.1二維數組名做函數參數分析:fun函數的關鍵語句是b[i]=a[i][i]-a[i][N-1-i];該語句完成了將4*4矩陣中對角線上的元素值減去本行的第N-1-i個元素的值,并將它保存到數組b中。具體功能如下圖:從上面的例子可以發(fā)現(xiàn),函數fun(inta[][N],intb[])的參數聲明中,a數組的寫法為a[][N],這是一個類似指針的用法,但是不能作為指針來用(即不能寫成*a[N]的形式)。當訪問二維數組元素的時候必須寫成a[i][j]的方式。選B。返回7.6.2指向第一維數組的指針變量做函數參數指向第一維數組的指針變量(實際上就是二維數組的首地址)也可以作為函數的參數。由于進入函數時對每個參數產生一個副本,如果將指向一維數組的指針變量作為函數的指針,則會產生該指針參數的副本。由于指針本身的特點是通過指針訪問對應的地址,所以即便是產生地址副本也能夠完全訪問相應地址所對應的內存。那么一般有兩種方式來將指向第一維數組的首地址作為參數。第一種方式是形參說明為數組的指針,實參為數組名。第二種方式是形參說明為數組的指針,實參為指向第一維數組的指針變量。比如數組a和函數fun聲明如下,且函數的參數為指向第一維數組的指針變量。int a[2][9],(*p)[9];int fun(int (*q)[9]);這個語句的等價語句為int fun(int (*)[9]);則調用函數fun時就可以使用兩種方式:第一種方式作為函數的參數時,調用函數的語句為:fun(a);.第二種方式作為函數的參數時,調用函數的語句為:p=a;fun(p);使用第二種方式時,注意到p是數組類型的指針變量,類型與形參的類型int (*q)[9]相同。賦值表達式p=a;使p指向二維數組a的第0行(即a數組的首地址,那么(p+1)表示第一行)。7.7字符串和指針7.7.1字符串的引用方式7.7.2字符指針做函數參數7.7.3字符指針變量和字符數組的比較7.7.1字符串的引用方式使用指針來引用字符串的方法與使用指針引用數組變量基本一致。必須看到引用字符串時的優(yōu)勢,可以使用標準庫函數相對比較方便地操作整個字符串。注意到使用指針引用數組時,不能方便地操作整個數組。如果需要逐個操作數組元素則通常需要使用循環(huán)來進行。例7.25使用指針操作字符串#include<stdio.h>#include<string.h>main(){ chars[10]="hello"; char*p=s;
printf("%s\n",s);
strcpy(p,"HI");
printf("%s",s);
printf("\n");}運行結果:helloHIPressanykeytocontinue從上面的例子可以發(fā)現(xiàn),熟練使用系統(tǒng)庫函數對于字符串的操作完全可以簡化。當然,也可以使用指針來逐個操作字符串中的每個字符。7.7.1字符串的引用方式例7.26有以下程序(注:字符a的ASCII碼值為97)。(2011年9月全國計算機等級考試二級C選擇題22題)#include<stdio.h>main(){ char*s={"abc"};
do{
printf("%d",*s%10); ++s; }while(*s);
printf("\n");}程序運行后的輸出結果是A.abcB.789C.7890D.979898分析:選B。注意:例7.26中字符串s的實際占用空間為4個字節(jié)(包含’\0’),而字符串的串長為3.返回7.7.2字符指針做函數參數同前述數組,指向字符串首地址的字符指針也可以做函數參數。下面來看一個例子:例7.27
指針作為函數的參數(2011年9月全國計算機等級考試二級C試題選擇題35題)#include<stdio.h>#include<string.h>voidfun(char*u,intn){ charx,*y1,*y2; y1=u; y2=u+n-1; while(y1<y2) { x=*y1; *y1=*y2; *y2=x; y1++; y2--; }}main(){ chara[]="1,2,3,4,5,6";
fun(a,strlen(a));
puts(a);}程序運行后的輸出結果是:A.654321B.115611C.153525D.123456答案:A7.7.2字符指針做函數參數如圖所示:while循環(huán)體內的執(zhí)行過程返回7.7.3字符指針變量和字符數組的比較字符指針變量與字符數組是有差別的,這兩個概念必須明確。首先,由例7.4我們可以知道指針只占用4個字節(jié)的存儲空間,字符指針也是這樣。而字符數組則占用數組長度乘以數組字符類型大小的存儲空間。即:字符指針占用4字節(jié)存儲空間字符數組占用的存儲空間字節(jié)數=(數組長度+1)sizeof(char)實際上,字符數組占用的存儲空間字節(jié)數就等于數組長度加一。那是因為字符類型只占用一個字節(jié)的存儲空間,上式sizeof(char)就等于1。并且注意到,(數組長度+1)中的那個”1”就是字符的結束符’\0’。來看一個例子:例7.28字符指針變量與字符數組各自占用的空間#include<stdio.h>main(){ char*p,a[10];
printf("字符指針類型變量占用的空間為:%d",sizeof(p));
printf("\n字符指針所指向的內存占用的空間為:%d",sizeof(*p));
printf("\n字符數組變量占用的空間為:%d",sizeof(a));
printf("\n");}運行結果:字符指針類型變量占用的空間為:4字符指針所指向的內存占用的空間為:1字符數組變量占用的空間為:107.7.3字符指針變量和字符數組的比較第二個問題就是:字符指針變量若不賦值,則其指向的空間是不確定的,如果強行訪問則必將導致內存泄露。但是字符數組若不賦值,系統(tǒng)仍然為其分配確定的存儲空間,即使不予賦值(數組中具體存放的內容不確定)仍然可以訪問,只是將數組的內容作為字符而已。注意到,如果不對數組進行賦值而強行訪問的話,則該數組沒有結束符標志’\0’,訪問的時候就不能使用string.h庫中的標準字符串函數訪問。那么為了防止下標越界,訪問的時候使用循環(huán)即可。例7.29字符指針不賦值的后果#include<stdio.h>main(){ char*p,a[10];
printf("\n字符數組的內容為:%s",a);
printf("字符指針類型變量所表示的內容為:%c",*p);
printf("\n");}該例需上機測試。7.8指向函數的指針7.8.1函數指針的定義和使用7.8.2用函數指針變量調用函數7.8.3用指向函數的指針作函數參數7.8.1函數指針的定義和使用函數指針:指針不僅能表示變量、表示數組與作為函數的參數,而且可以定義為指向函數的指針。指向函數的指針就是函數指針。函數指針是存放函數入口地址的指針變量。一個函數的入口地址由函數的函數名表示,他是函數在內存中的首條語句的入口地址。由于函數名是函數的入口地址,因此如果將函數名賦值給一個指針變量則該指針稱為指向該函數的函數指針。7.8.1函數指針的定義和使用在定義函數指針的時候,函數指針的類型必須與函數名的類型一致。函數指針的定義形式如下:類型說明(*標識符)(參數表)比如定義一個返回值為整型函數的指針可以寫成:int
(*p)(inta);。該語句可以說明為:p是一個指針,該指針指向具有一個整型參數且返回值為整型的函數。 使用函數指針也比較簡單,即將函數名賦值給同類型的函數指針即可。來看一個例子:例7.30函數指針的定義與使用#include<stdio.h>voidfun(inta){
printf("\n這是fun函數,帶入的參數是:%d",a);}main(){ void(*p)(inta); p=fun; (*p)(5);
printf("\n");}運行結果:這是fun函數,帶入的參數是:5返回7.8.2用函數指針變量調用函數函數指針的用法類似于變量指針的用法。注意到程序運行的時候是將程序裝入內存,則函數是存在內存入口地址的,因此若將函數名賦給指針變量,在類型匹配的前提下,通過相應的指針變量訪問內存是完全可行的。并且可以通過指針的變化來調用不同的函數。例7.31使用函數指針實現(xiàn)對不同函數的訪問。#include<stdio.h>intsub(int
x,inty){ returnx-y;}intadd(int
x,inty){ returnx+y;}main(){
int(*p)(int
a,intb);
int
a,b,result;
printf("\n請輸入兩個整數(兩個整數之間用一個空格隔開):");
scanf("%d%d",&a,&b); if(a>b) { p=sub; //語句1 result=(*p)(a,b); } else { p=add; //語句2 result=(*p)(a,b); }
printf("\n最終的結果為:%d",result);
printf("\n");}運行結果1:請輸入兩個整數(兩個整數之間用一個空格隔開):13最終的結果為:4運行結果2:請輸入兩個整數(兩個整數之間用一個空格隔開):31最終的結果為:2分析:程序功能:如果輸入的整數a大于b則調用減法函數,否則調用加法函數。上述代碼僅僅是作為一個調用函數指針的實例,通常情況下并不會這樣使用。在實際的使用當中會使用到的方式就是將函數指針作為函數的參數,只有這樣才能真正的做到“按需”靈活調用必須的函數。注意到,例子7.31的缺點在于語句1和語句2,我們使用了兩次賦值,這完全等于使用原來的函數,函數指針基本上成了個臨時變量。所以下面我們介紹使用的方法指向函數的指針作為函數的參數。返回7.8.3用指向函數的指針作函數參數使用當中,通常會用指向函數的指針作函數參數。這樣做是希望根據應用來調用所需要的函數,那么只要將所調用的函數作為另外一個函數的參數就可以解決問題。完全可以根據實際的需求的靈活調用。這里重寫例子7.31如下:例7.32將函數指針作為參數使用重寫例7.31#include<stdio.h>intsub(int
x,inty){ returnx-y;}intadd(int
x,inty){ returnx+y;}//定義一個處理器函數,其第三個參數定義為函數指針int
proccessor(int
x,int
y,int(*p)(int
a,intb)){ return(*p)(x,y);}main(){
int
a,b;
printf("\n請輸入兩個整數(兩個整數之間用一個空格隔開):");
scanf("%d%d",&a,&b);
printf("\n最終的結果為:%d",a>b?proccessor(a,b,sub):proccessor(a,b,add));
printf("\n");}運行結果1:請輸入兩個整數(兩個整數之間用一個空格隔開):13最終的結果為:4運行結果2:請輸入兩個整數(兩個整數之間用一個空格隔開):31最終的結果為:2分析:很明顯,這個代碼就大大簡化了,完全實現(xiàn)了例7.31的效果,而且代碼非常簡潔。那么這里對于函數指針作為函數參數的用法僅僅作一般性介紹。
7.8.3用指向函數的指針作函數參數例7.33請將以下程序中的函數聲明語句補充完整。(2009年3月全國計算機等級考試二級C語言填空題第12題)#include<stdio.h>int_____;main(){intx,y,(*p)();
scanf("%d%d",&x,&y);p=max; printf("%d\n",(*p)(x,y));}int
max(inta,intb){return(a>b?a:b);}答案:max(inta,intb)分析:從指針p的調用方式(*p)(x,y)與變量x,y為整型知空白處應當填寫一個函數的定義且函數有兩個整型參數,再由printf("%d\n",(*p)(x,y));的格式字符串%d知且該函數應有一個整數返回值,再由p=max語句知該函數名為max。綜合分析與下面
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 大學大學應用概率與統(tǒng)計課件
- 機械制圖模擬題+答案
- 介紹河南的英文課件演講
- 養(yǎng)老院老人生活照顧標準制度
- 養(yǎng)老院老人健康監(jiān)測人員福利待遇制度
- 托管中心個體工商戶勞務合同范本(2篇)
- 拆除施工承包協(xié)議書(2篇)
- 《藥膳常用的中藥》課件
- 對數的運算性質及其應用課件
- 2024年會議室場地出租協(xié)議3篇
- 教育信息化2.0時代教師新技能進階智慧樹知到期末考試答案章節(jié)答案2024年重慶對外經貿學院
- 江蘇開放大學本科財務管理專業(yè)060111馬克思主義基本原理期末試卷
- 2024年4月自考00155中級財務會計試題及答案
- 商務英語寫作1(山東聯(lián)盟)智慧樹知到期末考試答案章節(jié)答案2024年山東管理學院
- 2024年遼寧農業(yè)職業(yè)技術學院單招職業(yè)適應性測試題庫審定版
- 遇見朗讀者智慧樹知到期末考試答案章節(jié)答案2024年哈爾濱師范大學
- 中班音樂《小看戲》課件
- 電大財務大數據分析編程作業(yè)2
- 葡萄糖醛酸在藥物開發(fā)中的應用
- 體溫表水銀泄露的應急預案
- 導尿管相關尿路感染預防與控制技術指南(試行)-解讀
評論
0/150
提交評論