《指針》

《指針》

指針是C語言中廣泛使用的一種數據類型。運用指針編程是C語言最主要的風格之一。利用指針變數可以表示各種數據結構;能很方便地使用數組和字元串;並能象彙編語言一樣處理記憶體地址,從而編出精練而高效的程式。指針極大地豐富了C語言的功能。學習指針是學習C語言中最重要的一環,能否正確理解和使用指針是我們是否掌握C語言的一個標誌。同時,指針也是C語言中最為困難的一部分,在學習中除了要正確理解基本概念,還必須要多編程,上機調試。只要作到這些,指針也是不難掌握的。

【說明】:此教學設計任務由南京曉莊學院現代教育技術課程老師yqiong參與教師-維基星光計畫進行wiki教學而發布。此詞條為範例詞條,由吳佳豪選定主題並負責、參與現代教育技術課程的所有同學協作共同完成!

基本信息

科目:信息技術教學對象:大學一年級
課時:2教學環境:計算機網路教室

學習者特徵分析

在上一章的學習中,學生已經經歷了利用計算機解決問題的基本過程,領略了用計算機解題的奇妙之道,感悟了程式設計是其中的關鍵環節,對程式設計已經產生一定興趣,但由於在程式設計過程中,涉及數學、英語學科知識點較多,學生還沒有足夠的相關知識儲備且我校學生基礎薄弱,這樣主、客觀兩方面的因素造成在進行下一環節教學,會存在一定困難,難以深入開展下去。

教學內容

簡介

指針是C語言中廣泛使用的一種數據類型。 運用指針編程是C語言最主要的風格之一。利用指針變數可以表示各種數據結構; 能很方便地使用數組和字元串; 並能象彙編語言一樣處理記憶體地址,從而編出精練而高效的程式。指針極大地豐富了C語言的功能。 學習指針是學習C語言中最重要的一環, 能否正確理解和使用指針是我們是否掌握C語言的一個標誌。同時, 指針也是C語言中最為困難的一部分,在學習中除了要正確理解基本概念,還必須要多編程,上機調試。只要作到這些,指針也是不難掌握的。
指針的基本概念 在計算機中,所有的數據都是存放在存儲器中的。 一般把存儲器中的一個位元組稱為一個記憶體單元, 不同的數據類型所占用的記憶體單元數不等,如整型量占2個單元,字元量占1個單元等, 在第二章中已有詳細的介紹。為了正確地訪問這些記憶體單元, 必須為每個記憶體單元編上號。 根據一個記憶體單元的編號即可準確地找到該記憶體單元。記憶體單元的編號也叫做地址。 既然根據記憶體單元的編號或地址就可以找到所需的記憶體單元,所以通常也把這個地址稱為指針。 記憶體單元的指針和記憶體單元的內容是兩個不同的概念。 可以用一個通俗的例子來說明它們之間的關係。我們到銀行去存取款時, 銀行工作人員將根據我們的帳號去找我們的存款單, 找到之後在存單上寫入存款、取款的金額。在這裡,帳號就是存單的指針, 存款數是存單的內容。對於一個記憶體單元來說,單元的地址即為指針, 其中存放的數據才是該單元的內容。在C語言中, 允許用一個變數來存放指針,這種變數稱為指針變數。因此, 一個指針變數的值就是某個記憶體單元的地址或稱為某記憶體單元的指針。圖中,設有字元變數C,其內容為“K”(ASCII碼為十進制數 75),C占用了011A號單元(地址用十六進數表示)。設有指針變數P,內容為011A, 這種情況我們稱為P指向變數C,或說P是指向變數C的指針。 嚴格地說,一個指針是一個地址, 是一個常量。而一個指針變數卻可以被賦予不同的指針值,是變。 但在常把指針變數簡稱為指針。為了避免混淆,我們中約定:“指針”是指地址, 是常量,“指針變數”是指取值為地址的變數。 定義指針的目的是為了通過指針去訪問記憶體單元。
 
既然指針變數的值是一個地址, 那么這個地址不僅可以是變數的地址, 也可以是其它數據結構的地址。在一個指針變數中存放一

個數組或一個函式的首地址有何意義呢? 因為數組或函式都是連續存放的。通過訪問指針變數取得了數組或函式的首地址, 也就找到了該數組或函式。這樣一來, 凡是出現數組,函式的地方都可以用一個指針變數來表示, 只要該指針變數中賦予數組或函式的首地址即可。這樣做, 將會使程式的概念十分清楚,程式本身也精練,高效。在C語言中, 一種數據類型或數據結構往往都占有一組連續的記憶體單元。 用“地址”這個概念並不能很好地描述一種數據類型或數據結構, 而“指針”雖然實際上也是一個地址,但它卻是一個數據結構的首地址, 它是“指向”一個數據結構的,因而概念更為清楚,表示更為明確。 這也是引入“指針”概念的一個重要原因。

指針變數的類型說明

對指針變數的類型說明包括三個內容:
(1)指針類型說明,即定義變數為一個指針變數;
(2)指針變數名;
(3)變數值(指針)所指向的變數的數據類型。
其一般形式為: 類型說明符 *變數名;
其中,*表示這是一個指針變數,變數名即為定義的指針變數名,類型說明符表示本指針變數所指向的變數的數據類型。
例如: int *p1;表示p1是一個指針變數,它的值是某個整型變數的地址。 或者說p1指向一個整型變數。至於p1究竟指向哪一個整型變數, 應由向p1賦予的地址來決定。
再如:
staic int *p2; /*p2是指向靜態整型變數的指針變數*/
float *p3; /*p3是指向浮點變數的指針變數*/

char *p4; /*p4是指向字元變數的指針變數*/ 應該注意的是,一個指針變數只能指向同類型的變數,如P3 只能指向浮點變數,不能時而指向一個浮點變數, 時而又指向一個字元變數。

指針變數的賦值

指針變數同普通變數一樣,使用之前不僅要定義說明, 而且必須賦予具體的值。未經賦值的指針變數不能使用, 否則將造成系統混亂,甚至當機。指針變數的賦值只能賦予地址, 決不能賦予任何其它數據,否則將引起錯誤。在C語言中, 變數的地址是由編譯系統分配的,對用戶完全透明,用戶不知道變數的具體地址。 C語言中提供了地址運算符&來表示變數的地址。其一般形式為: & 變數名; 如&a變示變數a的地址,&b表示變數b的地址。 變數本身必須預先說明。設有指向整型變數的指針變數p,如要把整型變數a 的地址賦予p可以有以下兩種方式:
(1)指針變數初始化的方法 int a;
int *p=&a;
(2)賦值語句的方法 int a;
int *p;
p=&a;
不允許把一個數賦予指針變數,故下面的賦值是錯誤的: int *p;p=1000; 被賦值的指針變數前不能再加“*”說明符,如寫為*p=&a 也是錯誤的

指針變數的運算

指針變數可以進行某些運算,但其運算的種類是有限的。 它只能進行賦值運算和部分算術運算及關係運算。

1.指針運算符

(1)取地址運算符&
取地址運算符&是單目運算符,其結合性為自右至左,其功能是取變數的地址。在scanf函式及前面介紹指針變數賦值中,我們已經了解並使用了&運算符。
(2)取內容運算符*
取內容運算符*是單目運算符,其結合性為自右至左,用來表示指針變數所指的變數。在*運算符之後跟的變數必須是指針變數。需要注意的是指針運算符*和指針變數說明中的指針說明符* 不是一回事。在指針變數說明中,“*”是類型說明符,表示其後的變數是指針類型。而表達式中出現的“*”則是一個運算符用以表示指針變數所指的變數。
main(){
int a=5,*p=&a;
printf ("%d",*p);
}
......

表示指針變數p取得了整型變數a的地址。本語句表示輸出變數a的值。

2.指針變數的運算

(1)賦值運算
指針變數的賦值運算有以下幾種形式:
①指針變數初始化賦值,前面已作介紹。
②把一個變數的地址賦予指向相同數據類型的指針變數。例如:
int a,*pa;
pa=&a; /*把整型變數a的地址賦予整型指針變數pa*/
③把一個指針變數的值賦予指向相同類型變數的另一個指針變數。如:
int a,*pa=&a,*pb;
pb=pa; /*把a的地址賦予指針變數pb*/
由於pa,pb均為指向整型變數的指針變數,因此可以相互賦值。
④把數組的首地址賦予指向數組的指針變數。
例如: int a[5],*pa;
pa=a; (數組名表示數組的首地址,故可賦予指向數組的指針變數pa)
也可寫為:
pa=&a[0]; /*數組第一個元素的地址也是整個數組的首地址,
也可賦予pa*/
當然也可採取初始化賦值的方法:
int a[5],*pa=a;
⑤把字元串的首地址賦予指向字元類型的指針變數。例如: char *pc;pc="C language";或用初始化賦值的方法寫為: char *pc="C Language"; 這裡應說明的是並不是把整個字元串裝入指針變數, 而是把存放該字元串的字元數組的首地址裝入指針變數。 在後面還將詳細介紹。
⑥把函式的入口地址賦予指向函式的指針變數。例如: int (*pf)();pf=f; /*f為函式名*/

(2)加減算術運算
對於指向數組的指針變數,可以加上或減去一個整數n。設pa是指向數組a的指針變數,則pa+n,pa-n,pa++,++pa,pa--,--pa 運算都是合法的。指針變數加或減一個整數n的意義是把指針指向的當前位置(指向某數組元素)向前或向後移動n個位置。應該注意,數組指針變數向前或向後移動一個位置和地址加1或減1 在概念上是不同的。因為數組可以有不同的類型, 各種類型的數組元素所占的位元組長度是不同的。如指針變數加1,即向後移動1 個位置表示指針變數指向下一個數據元素的首地址。而不是在原地址基礎上加1。
例如:
int a[5],*pa;
pa=a; /*pa指向數組a,也是指向a[0]*/
pa=pa+2; /*pa指向a[2],即pa的值為&pa[2]*/ 指針變數的加減運算只能對數組指針變數進行, 對指向其它類型變數的指針變數作加減運算是毫無意義的。(3)兩個指針變數之間的運算只有指向同一數組的兩個指針變數之間才能進行運算, 否則運算毫無意義。
①兩指針變數相減
兩指針變數相減所得之差是兩個指針所指數組元素之間相差的元素個數。實際上是兩個指針值(地址) 相減之差再除以該數組元素的長度(位元組數)。例如pf1和pf2 是指向同一浮點數組的兩個指針變數,設pf1的值為2010H,pf2的值為2000H,而浮點數組每個元素占4個位元組,所以pf1-pf2的結果為(2000H-2010H)/4=4,表示pf1和 pf2之間相差4個元素。兩個指針變數不能進行加法運算。 例如, pf1+pf2是什麼意思呢?毫無實際意義。
②兩指針變數進行關係運算
指向同一數組的兩指針變數進行關係運算可表示它們所指數組元素之間的關係。例如:
pf1==pf2表示pf1和pf2指向同一數組元素
pf1>pf2表示pf1處於高地址位置
pf1<pf2表示pf2處於低地址位置
main(){
int a=10,b=20,s,t,*pa,*pb;
pa=&a;
pb=&b;
s=*pa+*pb;
t=*pa**pb;
printf("a=%d\nb=%d\na+b=%d\na*b=%d\n",a,b,a+b,a*b);
printf("s=%d\nt=%d\n",s,t);
}
......
說明pa,pb為整型指針變數
給指針變數pa賦值,pa指向變數a。
給指針變數pb賦值,pb指向變數b。
本行的意義是求a+b之和,(*pa就是a,*pb就是b)。
本行是求a*b之積。
輸出結果。
輸出結果。
......
指針變數還可以與0比較。設p為指針變數,則p==0表明p是空指針,它不指向任何變數;p!=0表示p不是空指針。空指針是由對指針變數賦予0值而得到的。例如: #define NULL 0 int *p=NULL; 對指針變數賦0值和不賦值是不同的。指針變數未賦值時,可以是任意值,是不能使用的。否則將造成意外錯誤。而指針變數賦0值後,則可以使用,只是它不指向具體的變數而已。
main(){
int a,b,c,*pmax,*pmin;
printf("input three numbers:\n");
scanf("%d%d%d",&a,&b,&c);
if(a>b){
pmax=&a;
pmin=&b;}
else{
pmax=&b;
pmin=&a;}
if(c>*pmax) pmax=&c;
if(c<*pmin) pmin=&c;
printf("max=%d\nmin=%d\n",*pmax,*pmin);
}
......
pmax,pmin為整型指針變數。
輸入提示。
輸入三個數字。
如果第一個數字大於第二個數字...
指針變數賦值
指針變數賦值
指針變數賦值
指針變數賦值
判斷並賦值
判斷並賦值
輸出結果
......

數組指針變數的說明和使用

 

指向數組的指針變數稱為數組指針變數。 在討論數組指針變數的說明和使用之前,我們先明確幾個關係。

一個數組是由連續的一塊記憶體單元組成的。 數組名就是這塊連續記憶體單元的首地址。一個數組也是由各個數組元素(下標變數) 組成的。每個數組元素按其類型不同占有幾個連續的記憶體單元。 一個數組元素的首地址也是指它所占有的幾個記憶體單元的首地址。 一個指針變數既可以指向一個數組,也可以指向一個數組元素, 可把數組名或第一個元素的地址賦予它。如要使指針變數指向第i號元素可以把i元素的首地址賦予它或把數組名加i賦予它。
設有實數組a,指向a的指針變數為pa,從圖6.3中我們可以看出有以下關係:
pa,a,&a[0]均指向同一單元,它們是數組a的首地址,也是0 號元素a[0]的首地址。pa+1,a+1,&a[1]均指向1號元素a[1]。類推可知a+i,a+i,&a[i]
指向i號元素a[i]。應該說明的是pa是變數,而a,&a[i]都是常量。在編程時應予以注意。
main(){
int a[5],i;
for(i=0;i<5;i++){
a[i]=i;
printf("a[%d]=%d\n",i,a[i]);
}
printf("\n");
}
主函式
定義一個整型數組和一個整型變數
循環語句
給數組賦值
列印每一個數組的值
......
輸出換行
......
數組指針變數說明的一般形式為:
類型說明符 * 指針變數名
其中類型說明符表示所指數組的類型。 從一般形式可以看出指向數組的指針變數和指向普通變數的指針變數的說明是相同的。
引入指針變數後,就可以用兩種方法來訪問數組元素了。
第一種方法為下標法,即用a[i]形式訪問數組元素。 在第四章中介紹數組時都是採用這種方法。
第二種方法為指針法,即採用*(pa+i)形式,用間接訪問的方法來訪問數組元素。
main(){
int a[5],i,*pa;
pa=a;
for(i=0;i<5;i++){
*pa=i;
pa++;
}
pa=a;
for(i=0;i<5;i++){
printf("a[%d]=%d\n",i,*pa);
pa++;
}
}
主函式
定義整型數組和指針
將指針pa指向數組a
循環
將變數i的值賦給由指針pa指向的a[]的數組單元
將指針pa指向a[]的下一個單元
......
指針pa重新取得數組a的首地址
循環
用數組方式輸出數組a中的所有元素
將指針pa指向a[]的下一個單元
......
......
下面,另舉一例,該例與上例本意相同,但是實現方式不同。
main(){
int a[5],i,*pa=a;
for(i=0;i<5;){
*pa=i;
printf("a[%d]=%d\n",i++,*pa++);
}
}
主函式
定義整型數組和指針,並使指針指向數組a
循環
將變數i的值賦給由指針pa指向的a[]的數組單元
用指針輸出數組a中的所有元素,同時指針pa指向a[]的下一個單元
......
......

數組名和數組指針變數作函式參數

在第五章中曾經介紹過用數組名作函式的實參和形參的問題。在學習指針變數之後就更容易理解這個問題了。 數組名就是數組的首地址,實參向形參傳送數組名實際上就是傳送數組的地址, 形參得到該地址後也指向同一數組。 這就好象同一件物品有兩個彼此不同的名稱一樣。同樣,指針變數的值也是地址, 數組指針變數的值即為數組的首地址,當然也可作為函式的參數使用。

float aver(float *pa);
main(){
float sco[5],av,*sp;
int i;
sp=sco;
printf("\ninput 5 scores:\n");
for(i=0;i<5;i++) scanf("%f",&sco[i]);
av=aver(sp);
printf("average score is %5.2f",av);
}
float aver(float *pa)
{
int i;
float av,s=0;
for(i=0;i<5;i++) s=s+*pa++;
av=s/5;
return av;

}

教學重點及難點

指向多維數組的指針變數

本小節以二維數組為例介紹多維數組的指針變數。


一、多維數組地址的表示方法

設有整型二維數組a[3][4]如下:
0 1 2 3
4 5 6 7
8 9 10 11
設數組a的首地址為1000,各下標變數的首地址及其值如圖所示。在第四章中介紹過, C語言允許把一個二維數組分解為多個一維數組來處理。因此數組a可分解為三個一維數組,即a[0],a[1],a[2]。每一個一維數組又含有四個元素。例如a[0]數組,含有a[0][0],a[0][1],a[0][2],a[0][3]四個元素。 數組及數組元素的地址表示如下:a是二維數組名,也是二維數組0行的首地址,等於1000。a[0]是第一個一維數組的數組名和首地址,因此也為1000。*(a+0)或*a是與a[0]等效的, 它表示一維數組a[0]0 號元素的首地址。 也為1000。&a[0][0]是二維數組a的0行0列元素首地址,同樣是1000。因此,a,a[0],*(a+0),*a,&a[0][0]是相等的。同理,a+1是二維數組1行的首地址,等於1008。a[1]是第二個一維數組的數組名和首地址,因此也為1008。 &a[1][0]是二維數組a的1行0列元素地址,也是1008。因此a+1,a[1],*(a+1),&a[1][0]是等同的。 由此可得出:a+i,a[i],*(a+i),&a[i][0]是等同的。 此外,&a[i]和a[i]也是等同的。因為在二維數組中不能把&a[i]理解為元素a[i]的地址,不存在元素a[i]。
C語言規定,它是一種地址計算方法,表示數組a第i行首地址。由此,我們得出:a[i],&a[i],*(a+i)和a+i也都是等同的。另外,a[0]也
可以看成是a[0]+0是一維數組a[0]的0號元素的首地址, 而a[0]+1則是a[0]的1號元素首地址,由此可得出a[i]+j則是一維數組a[i]的j號元素首地址,它等於&a[i][j]。由a[i]=*(a+i)得a[i]+j=*(a+i)+j,由於*(a+i)+j是二維數組a的i行j列元素的首地址。該元素的值等於*(*(a+i)+j)。
[Explain]#define PF "%d,%d,%d,%d,%d,\n"
main(){
static int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};
printf(PF,a,*a,a[0],&a[0],&a[0][0]);
printf(PF,a+1,*(a+1),a[1],&a[1],&a[1][0]);
printf(PF,a+2,*(a+2),a[2],&a[2],&a[2][0]);
printf("%d,%d\n",a[1]+1,*(a+1)+1);
printf("%d,%d\n",*(a[1]+1),*(*(a+1)+1));
}

二、多維數組的指針變數

把二維數組a 分解為一維數組a[0],a[1],a[2]之後,設p為指向二維數組的指針變數。可定義為: int (*p)[4] 它表示p是一個指針變數,它指向二維數組a 或指向第一個一維數組a[0],其值等於a,a[0],或&a[0][0]等。而p+i則指向一維數組a[i]。從前面的分析可得出*(p+i)+j是二維數組i行j 列的元素的地址,而*(*(p+i)+j)則是i行j列元素的值。
二維數組指針變數說明的一般形式為: 類型說明符 (*指針變數名)[長度] 其中“類型說明符”為所指數組的數據類型。“*”表示其後的變數是指針類型。 “長度”表示二維數組分解為多個一維數組時, 一維數組的長度,也就是二維數組的列數。應注意“(*指針變數名)”兩邊的括弧不可少,如缺少括弧則表示是指針數組(本章後面介紹),意義就完全不同了。
[Explain]main(){
static int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};
int(*p)[4];
int i,j;
p=a;
for(i=0;i<3;i++)
for(j=0;j<4;j++) printf("%2d ",*(*(p+i)+j));
}
'Expain字元串指針變數的說明和使用字元串指針變數的定義說明與指向字元變數的指針變數說明是相同的。只能按對指針變數的賦值不同來區別。 對指向字元變數的指針變數應賦予該字元變數的地址。如: char c,*p=&c;表示p是一個指向字元變數c的指針變數。而: char *s="C Language";則表示s是一個指向字元串的指針變數。把字元串的首地址賦予s。
請看下面一例。
main(){
char *ps;
ps="C Language";
printf("%s",ps);
}
運行結果為:
C Language
上例中,首先定義ps是一個字元指針變數, 然後把字元串的首地址賦予ps(應寫出整個字元串,以便編譯系統把該串裝入連續的一塊記憶體單元),並把首地址送入ps。程式中的: char *ps;ps="C Language";等效於: char *ps="C Language";輸出字元串中n個字元後的所有字元。
main(){
char *ps="this is a book";
int n=10;
ps=ps+n;
printf("%s\n",ps);
}
運行結果為:
book 在程式中對ps初始化時,即把字元串首地址賦予ps,當ps= ps+10之後,ps指向字元“b”,因此輸出為"book"。
main(){
char st[20],*ps;
int i;
printf("input a string:\n");
ps=st;
scanf("%s",ps);
for(i=0;ps[i]!="\0";i++)
if(ps[i]=="k"){
printf("there is a 'k' in the string\n");
break;
}
if(ps[i]=="\0") printf("There is no 'k' in the string\n");
}
本例是在輸入的字元串中查找有無‘k’字元。 下面這個例子是將指針變數指向一個格式字元串,用在printf函式中,用於輸出二維數組的各種地址表示的值。但在printf語句中用指針變數PF代替了格式串。 這也是程式中常用的方法。
main(){
static int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};
char *PF;
PF="%d,%d,%d,%d,%d\n";
printf(PF,a,*a,a[0],&a[0],&a[0][0]);
printf(PF,a+1,*(a+1),a[1],&a[1],&a[1][0]);
printf(PF,a+2,*(a+2),a[2],&a[2],&a[2][0]);
printf("%d,%d\n",a[1]+1,*(a+1)+1);
printf("%d,%d\n",*(a[1]+1),*(*(a+1)+1));
}
在下例是講解,把字元串指針作為函式參數的使用。要求把一個字元串的內容複製到另一個字元串中,並且不能使用strcpy函式。函式cprstr的形參為兩個字元指針變數。pss指向源字元串,pds指向目標字元串。表達式:
(*pds=*pss)!=`\0'
cpystr(char *pss,char *pds){
while((*pds=*pss)!="\0"){
pds++;
pss++; }
}
main(){
char *pa="CHINA",b[10],*pb;
pb=b;
cpystr(pa,pb);
printf("string a=%s\nstring b=%s\n",pa,pb);
}
在上例中,程式完成了兩項工作:一是把pss指向的源字元複製到pds所指向的目標字元中,二是判斷所複製的字元是否為`\0',若是則表明源字元串結束,不再循環。否則,pds和pss都加1,指向下一字元。在主函式中,以指針變數pa,pb為實參,分別取得確定值後調用cprstr函式。由於採用的指針變數pa和pss,pb和pds均指向同一字元串,因此在主函式和cprstr函式中均可使用這些字元串。也可以把cprstr函式簡化為以下形式:
cprstr(char *pss,char*pds)
{while ((*pds++=*pss++)!=`\0');}
即把指針的移動和賦值合併在一個語句中。 進一步分析還可發現`\0'的ASCⅡ碼為0,對於while語句只看錶達式的值為非0就循環,為0則結束循環,因此也可省去“!=`\0'”這一判斷部分,而寫為以下形式:
cprstr (char *pss,char *pds)
{while (*pdss++=*pss++);}
表達式的意義可解釋為,源字元向目標字元賦值, 移動指針,若所賦值為非0則循環,否則結束循環。這樣使程式更加簡潔。簡化後的程式如下所示。
cpystr(char *pss,char *pds){
while(*pds++=*pss++);
}
main(){
char *pa="CHINA",b[10],*pb;
pb=b;
cpystr(pa,pb);

printf("string a=%s\nstring b=%s\n",pa,pb);

使用字元串指針變數與字元數組的區別

用字元數組和字元指針變數都可實現字元串的存儲和運算。 但是兩者是有區別的。在使用時應注意以下幾個問題:
1. 字元串指針變數本身是一個變數,用於存放字元串的首地址。而字元串本身是存放在以該首地址為首的一塊連續的記憶體空間中並以‘\0’作為串的結束。字元數組是由於若干個數組元素組成的,它可用來存放整個字元串。
2. 對字元數組作初始化賦值,必須採用外部類型或靜態類型,如: static char st[]={“C Language”};而對字元串指針變數則無此限制,如: char *ps="C Language";
3. 對字元串指針方式 char *ps="C Language";可以寫為: char *ps; ps="C Language";而對數組方式:
static char st[]={"C Language"};
不能寫為:
char st[20];st={"C Language"};
而只能對字元數組的各元素逐個賦值。
從以上幾點可以看出字元串指針變數與字元數組在使用時的區別,同時也可看出使用指針變數更加方便。前面說過,當一個指針變數在未取得確定地址前使用是危險的,容易引起錯誤。但是對指針變數直接賦值是可以的。因為C系統對指針變數賦值時要給以確定的地址。因此,
char *ps="C Langage";
或者 char *ps;
ps="C Language";都是合法的。

函式指針變數

在C語言中規定,一個函式總是占用一段連續的記憶體區, 而函式名就是該函式所占記憶體區的首地址。 我們可以把函式的這個首地址(或稱入口地址)賦予一個指針變數, 使該指針變數指向該函式。然後通過指針變數就可以找到並調用這個函式。 我們把這種指向函式的指針變數稱為“函式指針變數”。
函式指針變數定義的一般形式為:
類型說明符 (*指針變數名)();
其中“類型說明符”表示被指函式的返回值的類型。“(* 指針變數名)”表示“*”後面的變數是定義的指針變數。 最後的空括弧表示指針變數所指的是一個函式。
例如: int (*pf)();
表示pf是一個指向函式入口的指針變數,該函式的返回值(函式值)是整型。
下面通過例子來說明用指針形式實現對函式調用的方法。
int max(int a,int b){
if(a>b)return a;
else return b;
}
main(){
int max(int a,int b);
int(*pmax)();
int x,y,z;
pmax=max;
printf("input two numbers:\n");
scanf("%d%d",&x,&y);
z=(*pmax)(x,y);
printf("maxmum=%d",z);
}
從上述程式可以看出用,函式指針變數形式調用函式的步驟如下:1. 先定義函式指針變數,如後一程式中第9行 int (*pmax)();定義pmax為函式指針變數。
2. 把被調函式的入口地址(函式名)賦予該函式指針變數,如程式中第11行 pmax=max;
3. 用函式指針變數形式調用函式,如程式第14行 z=(*pmax)(x,y); 調用函式的一般形式為: (*指針變數名) (實參表)使用函式指針變數還應注意以下兩點:
a. 函式指針變數不能進行算術運算,這是與數組指針變數不同的。數組指針變數加減一個整數可使指針移動指向後面或前面的數組元素,而函式指針的移動是毫無意義的。
b. 函式調用中"(*指針變數名)"的兩邊的括弧不可少,其中的*不應該理解為求值運算,在此處它只是一種表示符號。

指針型函式

前面我們介紹過,所謂函式類型是指函式返回值的類型。 在C語言中允許一個函式的返回值是一個指針(即地址), 這種返回指針值的函式稱為指針型函式。
定義指針型函式的一般形式為:
類型說明符 *函式名(形參表)
{
…… /*函式體*/
}
其中函式名之前加了“*”號表明這是一個指針型函式,即返回值是一個指針。類型說明符表示了返回的指針值所指向的數據類型。
如:
int *ap(int x,int y)
{
...... /*函式體*/
}
表示ap是一個返回指針值的指針型函式, 它返回的指針指向一個整型變數。下例中定義了一個指針型函式 day_name,它的返回值指向一個字元串。該函式中定義了一個靜態指針數組name。name 數組初始化賦值為八個字元串,分別表示各個星期名及出錯提示。形參n表示與星期名所對應的整數。在主函式中, 把輸入的整數i作為實參, 在printf語句中調用day_name函式並把i值傳送給形參 n。day_name函式中的return語句包含一個條件表達式, n 值若大於7或小於1則把name[0] 指針返回主函式輸出出錯提示字元串“illegal day”。否則返回主函式輸出對應的星期名。主函式中的第7行是個條件語句,其語義是,如輸入為負數(i<0)則中止程式運行退出程式。exit是一個庫函式,exit(1)表示發生錯誤後退出程式, exit(0)表示正常退出。
應該特別注意的是函式指針變數和指針型函式這兩者在寫法和意義上的區別。如int(*p)()和int *p()是兩個完全不同的量。int(*p)()是一個變數說明,說明p 是一個指向函式入口的指針變數,該函式的返回值是整型量,(*p)的兩邊的括弧不能少。int *p() 則不是變數說明而是函式說明,說明p是一個指針型函式,其返回值是一個指向整型量的指針,*p兩邊沒有括弧。作為函式說明, 在括弧內最好寫入形式參數,這樣便於與變數說明區別。 對於指針型函式定義,int *p()只是函式頭部分,一般還應該有函式體部分。
main(){
int i;
char *day_name(int n);
printf("input Day No:\n");
scanf("%d",&i);
if(i<0) exit(1);
printf("Day No:%2d-->%s\n",i,day_name(i));
}
char *day_name(int n){
static char *name[]={ "Illegal day",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday"};
return((n<1||n>7) ? name[0] : name[n]);
}
本程式是通過指針函式,輸入一個1~7之間的整數, 輸出對應的星期名。指針數組的說明與使用一個數組的元素值為指針則是指針數組。 指針數組是一組有序的指針的集合。 指針數組的所有元素都必須是具有相同存儲類型和指向相同數據類型的指針變數。

指針數組說明的一般形式為: 類型說明符*數組名[數組長度]
其中類型說明符為指針值所指向的變數的類型。例如: int *pa[3] 表示pa是一個指針數組,它有三個數組元素, 每個元素值都是一個指針,指向整型變數。通常可用一個指針數組來指向一個二維數組。 指針數組中的每個元素被賦予二維數組每一行的首地址, 因此也可理解為指向一個一維數組。圖6—6表示了這種關係。
int a[3][3]={1,2,3,4,5,6,7,8,9};
int *pa[3]={a[0],a[1],a[2]};
int *p=a[0];
main(){
int i;
for(i=0;i<3;i++)
printf("%d,%d,%d\n",a[i][2-i],*a[i],*(*(a+i)+i));
for(i=0;i<3;i++)
printf("%d,%d,%d\n",*pa[i],p[i],*(p+i));
}
本例程式中,pa是一個指針數組,三個元素分別指向二維數組a的各行。然後用循環語句輸出指定的數組元素。其中*a[i]表示i行0列元素值;*(*(a+i)+i)表示i行i列的元素值;*pa[i]表示i行0列元素值;由於p與a[0]相同,故p[i]表示0行i列的值;*(p+i)表示0行i列的值。讀者可仔細領會元素值的各種不同的表示方法。 應該注意指針數組和二維數組指針變數的區別。 這兩者雖然都可用來表示二維數組,但是其表示方法和意義是不同的。
二維數組指針變數是單個的變數,其一般形式中"(*指針變數名)"兩邊的括弧不可少。而指針數組類型表示的是多個指針( 一組有序指針)在一般形式中"*指針數組名"兩邊不能有括弧。例如: int (*p)[3];表示一個指向二維數組的指針變數。該二維數組的列數為3或分解為一維數組的長度為3。 int *p[3] 表示p是一個指針數組,有三個下標變數p[0],p[1],p[2]均為指針變數。
指針數組也常用來表示一組字元串, 這時指針數組的每個元素被賦予一個字元串的首地址。 指向字元串的指針數組的初始化更為簡單。例如在例6.20中即採用指針數組來表示一組字元串。 其初始化賦值為:
char *name[]={"Illagal day",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday"};
完成這個初始化賦值之後,name[0]即指向字元串"Illegal day",name[1]指?quot;Monday"......。
指針數組也可以用作函式參數。在本例主函式中,定義了一個指針數組name,並對name 作了初始化賦值。其每個元素都指向一個字元串。然後又以name 作為實參調用指針型函式day name,在調用時把數組名 name 賦予形參變數name,輸入的整數i作為第二個實參賦予形參n。在day name函式中定義了兩個指針變數pp1和pp2,pp1被賦予name[0]的值(即*name),pp2被賦予name[n]的值即*(name+ n)。由條件表達式決定返回pp1或pp2指針給主函式中的指針變數ps。最後輸出i和ps的值。

教學策略

本節內容以老師授課為主
在教學過程中會有小組討論環節,討論老師提出的具有探索性的問題,並由小組推薦一人進行總結回答
在教學快結束時,會抽學生到講台上操作,通過投影儀放映操作步驟。

相關詞條

相關搜尋

熱門詞條

聯絡我們