變量在內(nèi)存存放是有地址的,數(shù)組在內(nèi)存存放也同樣具有地址。對(duì)數(shù)組來(lái)說(shuō),數(shù)組名就是數(shù)組在內(nèi)存安放的首地址。指針變量是用于存放變量的地址,可以指向變量,當(dāng)然也可存放數(shù)組的首址或數(shù)組元素的地址,這就是說(shuō),指針變量可以指向數(shù)組或數(shù)組元素,對(duì)數(shù)組而言,數(shù)組和數(shù)組元素的引用,也同樣可以使用指針變量。下面就分別介紹指針與不同類型的數(shù)組。
6.4.1指針與一維數(shù)組
假設(shè)我們定義一個(gè)一維數(shù)組,該數(shù)組在內(nèi)存會(huì)有系統(tǒng)分配的一個(gè)存儲(chǔ)空間,其數(shù)組的名字就是數(shù)組在內(nèi)存的首地址。若再定義一個(gè)指針變量,并將數(shù)組的首址傳給指針變量,則該指針就指向了這個(gè)一維數(shù)組。我們說(shuō)數(shù)組名是數(shù)組的首地址,也就是數(shù)組的指針。而定義的指針變量就是指向該數(shù)組的指針變量。對(duì)一維數(shù)組的引用,既可以用傳統(tǒng)的數(shù)組元素的下標(biāo)法,也可使用指針的表示方法。
inta[10],*ptr;/*定義數(shù)組與指針變量*/
做賦值操作:ptr=a;或ptr=&a[0];
則ptr就得到了數(shù)組的首址。其中,a是數(shù)組的首地址,&a[0]是數(shù)組元素a[0]的地址,由于a[0]的地址就是數(shù)組的首地址,所以,兩條賦值操作效果完全相同。指針變量ptr就是指向數(shù)組a的指針變量。
若ptr指向了一維數(shù)組,現(xiàn)在看一下C規(guī)定指針對(duì)數(shù)組的表示方法:
1)ptr+n與a+n表示數(shù)組元素a[n]的地址,即&a[n]。對(duì)整個(gè)a數(shù)組來(lái)說(shuō),共有10個(gè)元素,n的取值為0~9,則數(shù)組元素的地址就可以表示為ptr+0~ptr+9或a+0~a+9,與&a[0]~&a[9]保持一致。
2)知道了數(shù)組元素的地址表示方法,*(ptr+n)和*(a+n)就表示為數(shù)組的各元素即等效于a[n]。
3)指向數(shù)組的指針變量也可用數(shù)組的下標(biāo)形式表示為ptr[n],其效果相當(dāng)于*(ptr+n)。
[例6-5]/*以下標(biāo)法輸入輸出數(shù)組各元素。
下面從鍵盤輸入10個(gè)數(shù),以數(shù)組的不同引用形式輸出數(shù)組各元素的值。
#include<stdio.h>
main()
{
intn,a[10],*ptr=a;
for(n=0;n<=9;n++)
scanf(“%d”,&a[n]);
printf(“1——output!n”);
for(n=0;n<=9;n++)
printf(“%4d”,a[n]);
printf(“n”);
}
運(yùn)行程序:
RUN
1234567890?
1——output!
1234567890
[例6-6]采用指針變量表示的地址法輸入輸出數(shù)組各元素。
#include<stdio.h>
main()
{
int n,a[10],*ptr=a;/*定義時(shí)對(duì)指針變量初始化*/
for(n=0;n<=9;n++)
scanf(“%d”,ptr+n);
print f(“2——output!n”);
for(n=0;n<=9;n++)
print f(“%4d”,*(ptr+n));
print f(“n”);
}
運(yùn)行程序:
RUN
1234567890?
2——output!
1234567890
[例6-7]采用數(shù)組名表示的地址法輸入輸出數(shù)組各元素。
main()
{
int n,a[10],*ptr=a;
for(n=0;n<=9;n++)
scanf(“%d”,a+n);
print f(“3——output!n”);
for(n=0;n<=9;n++)
print f(“%4d”,*(a+n));
print f(“n”);
}
運(yùn)行程序:
RUN
1234567890?
3——output!
1234567890
[例6-8]用指針表示的下標(biāo)法輸入輸出數(shù)組各元素。
main()
{
int n,a[10],*ptr=a;
for(n=0;n<=9;n++)
scanf(“%d”,&ptr[n]);
print f(“4——output!n”);
for(n=0;n<=9;n++)
print f(“%4d”,ptr[n]);
print f(“n”);
}
運(yùn)行程序:
RUN
1234567890
4—-output!
1234567890
[例6-9]利用指針?lè)ㄝ斎胼敵鰯?shù)組各元素。
main()
{
int n,a[10],*ptr=a;
for(n=0;n<=9;n++)
scanf(“%d”,ptr++);
print f(“5——output!n”);
ptr=a;/*指針變量重新指向數(shù)組首址*/
for(n=0;n<=9;n++)
print f(“%4d”,*ptr++);
print f(“n”);
}
運(yùn)行程序:
RUN
1234567890?
5—–output!
1234567890
在程序中要注意*ptr++所表示的含義。*ptr表示指針?biāo)赶虻淖兞?;ptr++表示指針?biāo)赶虻淖兞康刂芳?個(gè)變量所占字節(jié)數(shù),具體地說(shuō),若指向整型變量,則指針值加2,若指向?qū)嵭?,則加4,依此類推。而print f(“%4d”,*ptr++)中,*ptr++所起作用為先輸出指針指向的變量的值,然后指針變量加1。循環(huán)結(jié)束后,指針變量指向如圖6-6所示:
指針變量的值在循環(huán)結(jié)束后,指向數(shù)組的尾部的后面。假設(shè)元素a[9]的地址為1000,整型占2字節(jié),則ptr的值就為1002。請(qǐng)思考下面的程序段:
main()
{
int n,a[10],*ptr=a;
for(n=0;n<=9;n++)
scanf(“%d”,ptr++);
print f(“4——output!n”);
for(n=0;n<=9;n++)
print f(“%4d”,*ptr++);
print f(“n”);
}
程序與例6-9相比,只少了賦值語(yǔ)句ptr=a;程序的運(yùn)行結(jié)果還相同嗎?
6.4.2指針與二維數(shù)組
定義一個(gè)二維數(shù)組:
inta[3][4];
表示二維數(shù)組有三行四列共12個(gè)元素,在內(nèi)存中按行存放,存放形式為圖6-7:
其中a是二維數(shù)組的首地址,&a[0][0]既可以看作數(shù)組0行0列的首地址,同樣還可以看作是二維數(shù)組的首地址,a[0]是第0行的首地址,當(dāng)然也是數(shù)組的首地址。同理a[n]就是第n行的首址;&a[n][m]就是數(shù)組元素a[n][m]的地址。
既然二維數(shù)組每行的首地址都可以用a[n]來(lái)表示,我們就可以把二維數(shù)組看成是由n行一維數(shù)組構(gòu)成,將每行的首地址傳遞給指針變量,行中的其余元素均可以由指針來(lái)表示。下面的圖6-8給出了指針與二維數(shù)組的關(guān)系:
我們定義的二維數(shù)組其元素類型為整型,每個(gè)元素在內(nèi)存占兩個(gè)字節(jié),若假定二維數(shù)組從1000單元開(kāi)始存放,則以按行存放的原則,數(shù)組元素在內(nèi)存的存放地址為1000~1022。
用地址法來(lái)表示數(shù)組各元素的地址。對(duì)元素a[1][2],&a[1][2]是其地址,a[1]+2也是其地址。分析a[1]+1與a[1]+2的地址關(guān)系,它們地址的差并非整數(shù)1,而是一個(gè)數(shù)組元素的所占位置2,原因是每個(gè)數(shù)組元素占兩個(gè)字節(jié)。
對(duì)0行首地址與1行首地址a與a+1來(lái)說(shuō),地址的差同樣也并非整數(shù)1,是一行,四個(gè)元素占的字節(jié)數(shù)8。
由于數(shù)組元素在內(nèi)存的連續(xù)存放。給指向整型變量的指針傳遞數(shù)組的首地址,則該指針指向二維數(shù)組。
int *ptr,a[3][4];
若賦值:ptr=a;則用ptr++就能訪問(wèn)數(shù)組的各元素。
[例6-10]用地址法輸入輸出二維數(shù)組各元素。
#include<stdio.h>
main()
{
int a[3][4];
int i,j;
for(i=0;i<3;i++)
for(j=0;j<4;j++)
scanf(“%d”,a[i]+j);/*地址法*/
for(i=0;i<3;i++)
{
for(j=0;j<4;j++)
printf(“%4d”,*(a[i]+j));/**(a[i]+是j地)址法所表示的數(shù)組元素*/
printf(“n”);
}
}
運(yùn)行程序:
RUN
1 2 3 4 5 6 7 8 9 10 11 12
1 2 3 4
5 6 7 8
9 10 11 12
[例6-11]用指針?lè)ㄝ斎胼敵龆S數(shù)組各元素。
#include<stdio.h>
main()
{
int a[3][4],*ptr;
int i,j;
ptr=a[0];
for(i=0;i<3;i++)
for(j=0;j<4;j++)
scanf(“%d”,ptr++);/*指針的表示方法*/
ptr=a[0];
for(i=0;i<3;i++)
{
for(j=0;j<4;j++)
printf(“%4d”,*ptr++);
printf(“n”);
}
}
運(yùn)行程序:
RUN
1 2 3 4 5 6 7 8 9 10 11 12
1 2 3 4
6.4.3 數(shù)組指針作函數(shù)的參數(shù)
學(xué)習(xí)了指向一維和二維數(shù)組指針變量的定義和正確引用后,我們現(xiàn)在學(xué)習(xí)用指針變量作
函數(shù)的參數(shù)。
[例6-12] 調(diào)用子程序,實(shí)現(xiàn)求解一維數(shù)組中的最大元素。
我們首先假設(shè)一維數(shù)組中下標(biāo)為0的元素是最大和用指針變量指向該元素。后續(xù)元素與該
元素一一比較,若找到更大的元素,就替換。子程序的形式參數(shù)為一維數(shù)組,實(shí)際參數(shù)是指
向一維數(shù)組的指針。
# include <stdio.h>
m a i n ( )
{
int sub_max(); / * 函數(shù)聲明* /
int n,a[10],*ptr=a; / *定義變量,并使指針指向數(shù)組* /
int max;
f o r ( n = 0 ; n < = i – 1 ; n + + ) / *輸入數(shù)據(jù)* /
s c a n f ( ” % d ” , & a [ n ] ) ;
m a x = s u b _ m a x ( p t r , 1 0 ) ; / * 函數(shù)調(diào)用,其實(shí)參是指針* /
p r i n t f ( ” m a x = % d n ” , m a x ) ;
}
int sub_max(b,i) / * 函數(shù)定義,其形參為數(shù)組* /
int b[],i;
{
int temp,j;
t e m p = b [ 0 ] ;
f o r ( j = 1 ; j < = 9 ; j + + )
if(temp<b[j]) temp=b[j];
return temp;
}
程序的m a i n ( )函數(shù)部分,定義數(shù)組a 共有1 0個(gè)元素,由于將其首地址傳給了p t r,則指針
變量ptr 就指向了數(shù)組,調(diào)用子程序,再將此地址傳遞給子程序的形式參數(shù)b,這樣一來(lái),b
數(shù)組在內(nèi)存與a 數(shù)組具有相同地址,即在內(nèi)存完全重合。在子程序中對(duì)數(shù)組b 的操作,與操
作數(shù)組a 意義相同。其內(nèi)存中虛實(shí)結(jié)合的示意如圖6 – 9所示。
m a i n ( )函數(shù)完成數(shù)據(jù)的輸入,調(diào)用子程序并輸出運(yùn)行結(jié)果。s u b _ m a x ( )函數(shù)完成對(duì)數(shù)組元
素找最大的過(guò)程。在子程序內(nèi)數(shù)組元素的表示采用下標(biāo)法。運(yùn)行程序:
R U N
1 3 5 7 9 2 4 6 8 0
m a x = 9
[例6-13] 上述程序也可采用指針變量作子程序的形式參數(shù)。
# include <stdio.h>
m a i n ( )
{
int sub_max();
int n,a[10],*ptr=a;
int max;
f o r ( n = 0 ; n < = 9 ; n + + )
s c a n f ( ” % d ” , & a [ n ] ) ;
m a x = s u b _ m a x ( p t r , 1 0 ) ;
p r i n t f ( ” m a x = % d n ” , m a x ) ;
}
int sub_max(b,i) / *形式參數(shù)為指針變量* /
int *b,i;
{
int temp,j;
t e m p = b [ 0 ] ; / *數(shù)組元素指針的下標(biāo)法表示* /
f o r ( j = 1 ; j < = i – 1 ; j + + )
if(temp<b[j]) temp=b[j];
return temp;
}
在子程序中,形式參數(shù)是指針,調(diào)用程序的實(shí)際參數(shù)p t r為指向一維數(shù)組a的指針,虛實(shí)結(jié)
合,子程序的形式參數(shù)b得到p t r的值,指向了內(nèi)存的一維數(shù)組。數(shù)組元素采用下標(biāo)法表示,即
一維數(shù)組的頭指針為b,數(shù)組元素可以用b [ j ]表示。其內(nèi)存中虛實(shí)參數(shù)的結(jié)合如圖6 – 1 0所示。
運(yùn)行程序:
R U N
1 3 5 7 9 2 4 6 8 0?
m a x = 9
[例6-14] 上述程序的子程序中,數(shù)組元素還可以用指針表示。
# include <stdio.h>
m a i n ( )
{
int sub_max();
int n,a[10],*ptr=a;
int max;
f o r ( n = 0 ; n < = 9 ; n + + )
s c a n f ( ” % d ” , & a [ n ] ) ;
m a x = s u b _ m a x ( p t r , 1 0 ) ;
p r i n t f ( ” m a x = % d n ” , m a x ) ;
}
int sub_max(b,i)/ *子程序定義* /
int *b,i;
{
int temp,j;
t e m p = * b + + ;
f o r ( j = 1 ; j < = i – 1 ; j + + )
if(temp<*b) temp=*b++;
return temp;
}
在程序中,賦值語(yǔ)句t e m p = * b + +;可以分解為:t e m p = * b;b + +;兩句,先作t e m p = * b;后
作b + +;程序的運(yùn)行結(jié)果與上述完全相同。
對(duì)上面的程序作修改,在子程序中不僅找最大元素,同時(shí)還要將元素的下標(biāo)記錄下來(lái)。
# include <stdio.h>
m a i n ( )
{
int *max();/* 函數(shù)聲明* /
int n,a[10],*s,i;
f o r ( i = 0 ; i < 1 0 ; i + + ) / * 輸入數(shù)據(jù)* /
scanf(“%d”,a+i);
s = m a x ( a , 1 0 ) ; / *函數(shù)調(diào)用* /
p r i n t f ( ” m a x = % d , i n d e x = % d n ” , * s , s – a ) ;
}
int *max(a,n) / *定義返回指針的函數(shù)* /
int *a,n;
{
int *p,*t; / * p 用于跟蹤數(shù)組,t用于記錄最大值元素的地址* /
f o r ( p = a , t = a ; p – a < n ; p + + )
if(*p>*t) t=p;
return t;
}
在m a x()函數(shù)中,用p – a < n來(lái)控制循環(huán)結(jié)束, a是數(shù)組首地址, p用于跟蹤數(shù)組元素的地址,p – a正好是所跟蹤元素相對(duì)數(shù)組頭的距離,或者說(shuō)是所跟蹤元素相對(duì)數(shù)組頭的元素個(gè)數(shù),所以在m a i n ( )中,最大元素的下標(biāo)就是該元素的地址與數(shù)組頭的差,即s – a。運(yùn)行程序:
R U N
1 3 5 7 9 2 4 6 8 0?
m a x = 9 , i n d e x = 4
[例6-15] 用指向數(shù)組的指針變量實(shí)現(xiàn)一維數(shù)組的由小到大的冒泡排序。編寫(xiě)三個(gè)函數(shù)用于輸入數(shù)據(jù)、數(shù)據(jù)排序、數(shù)據(jù)輸出。
在第5章的例題中,我們介紹過(guò)選擇法排序及算法,此例再介紹冒泡排序算法。為了將一組n個(gè)無(wú)序的數(shù)整理成由小到大的順序,將其放入一維數(shù)組a [ 0 ]、a [ 1 ]. . .a [ n – 1 ]。冒泡算法如下:
(開(kāi)序)
① 相鄰的數(shù)組元素依次進(jìn)行兩兩比較,即a [ 0 ]與a [ 1 ]比、a [ 1 ]與a [ 2 ]比. . . a [ n – 2 ]與a [ n – 1 ]比,通過(guò)交換保證數(shù)組的相鄰兩個(gè)元素前者小,后者大。此次完全的兩兩比較,能免實(shí)現(xiàn)a [ n – 1 ]成為數(shù)組中最大。
② 余下n – 1個(gè)元素,按照上述原則進(jìn)行完全兩兩比較,使a [ n – 2 ]成為余下n – 1個(gè)元素中最大。
③ 進(jìn)行共計(jì)n – 1趟完全的兩兩比較,使全部數(shù)據(jù)整理有序。
下面給出一趟排序的處理過(guò)程:
4個(gè)元素進(jìn)行3次兩兩比較,得到一個(gè)最大元素。若相鄰元素表示為a [ j ]和a [ j + 1 ],用指針
變量P指向數(shù)組,則相鄰元素表示為* ( P + j )和* ( P + j + 1 )程序?qū)崿F(xiàn)如下:
# include<stdio.h>
#define N 10
m a i n ( )
{
void input(); / *函數(shù)聲明* /
void sort();
void output();
int a[N],*p; / *定義一維數(shù)組和指針變量* /
i n p u t ( a , N ) ; / *數(shù)據(jù)輸入函數(shù)調(diào)用,實(shí)參a是數(shù)組名* /
p = a ; / *指針變量指向數(shù)組的首地址* /
s o r t ( p , N ) ; / *排序,實(shí)參p是指針變量* /
o u t p u t ( p , N ) ; / *輸出,實(shí)參p是指針變量* /
}
void input(arr,n) / *無(wú)需返回值的輸入數(shù)據(jù)函數(shù)定義,形參a r r 是數(shù)組* /
int arr[],n;
{
int i;
printf(“input data:n”);
for ( i = 0 ; i < n ; i + + ) / *采用傳統(tǒng)的下標(biāo)法*/
s c a n f ( ” % d ” , & a r r [ i ] ) ;
}
void sort(ptr,n) / *冒泡排序,形參ptr 是指針變量* /
int *ptr,n;
{
int i,j,t;
for ( i = 0 ; i < n – 1 ; i + + )
for ( j = 0 ; j < n – 1 – i ; j + + )
if (*(ptr+j)>*(ptr+j+1))/相*臨兩個(gè)元素進(jìn)行比較*/
{
t = * ( ptr + j ) ; / *兩個(gè)元素進(jìn)行交換* /
* ( ptr + j ) = * ( ptr + j + 1 ) ;
* ( ptr + j + 1 ) = t ;
}
}
void output(arr,n) / *數(shù)據(jù)輸出* /
int arr[],n;
{
int i,*ptr=arr; / *利用指針指向數(shù)組的首地址* /
printf(“output data:n”);
for ( ; ptr – a r r < n ; ptr + + ) / *輸出數(shù)組的n個(gè)元素* /
printf ( ” % 4 d ” , * ptr ) ;
printf ( ” n ” ) ;
}
由于C程序的函數(shù)調(diào)用是采用傳值調(diào)用,即實(shí)際參數(shù)與形式參數(shù)相結(jié)合時(shí),實(shí)參將值傳給形式參數(shù),所以當(dāng)我們利用函數(shù)來(lái)處理數(shù)組時(shí),如果需要對(duì)數(shù)組在子程序中修改,只能傳遞數(shù)組的地址,進(jìn)行傳地址的調(diào)用,在內(nèi)存相同的地址區(qū)間進(jìn)行數(shù)據(jù)的修改。在實(shí)際的應(yīng)用中,
如果需要利用子程序?qū)?shù)組進(jìn)行處理,函數(shù)的調(diào)用利用指向數(shù)組(一維或多維)的指針作參數(shù),無(wú)論是實(shí)參還是形參共有下面四種情況:
我們知道,二維數(shù)組在內(nèi)存中是按行存放,假定我們定義二維數(shù)組和指針如下:
int a[3][4],* p = a [ 0 ] ;
則指針p就指向二維數(shù)組。其在內(nèi)存的存放情況如圖6 – 11所示。
從上述存放情況來(lái)看,若把二維數(shù)組的首地址傳遞給指針p,則映射過(guò)程如圖6 – 11
所示。我們只要找到用p所表示的一維數(shù)組中最大的元素及下標(biāo),就可轉(zhuǎn)換為在二維數(shù)組中的
行列數(shù)。
# include<stdio.h>
m a i n ( )
{
int a[3][4],*ptr,i,j,max,maxi,maxj;
/ * m a x 是數(shù)組的最大, m a x i 是最大元素所在行, m a x j 是最大元素所在列* /
f o r ( i = 0 ; i < 3 ; i + + )
f o r ( j = 0 ; j < 4 ; j + + )
s c a n f ( ” % d ” , & a [ i ] [ j ] ) ;
p t r = a [ 0 ] ; / * 將二維數(shù)組的首地址傳遞給指針變量* /
m a x _ a r r ( p t r , & m a x , & m a x i , 1 2 ) ;
m a x j = m a x i % 4 ; / * 每行有四個(gè)元素,求該元素所在列* /
m a x i = m a x i / 4 ; / * 求該元素所在行* /
printf(“max=%d,maxi=%d,maxj=%d”,max,maxi,maxj);
}
int max_arr(b,p1,p2,n)
int *b,*p1,*p2,n;
/ * b 指向二維數(shù)組的指針, p 1指向最大值,p 2 指向最大值在一維數(shù)組中的位置, * /
/ * n 是數(shù)組的大小* /
{
int i;
*p1=b[0]; *p1=0;
f o r ( i = 1 ; i < n ; i + + ) / * 找最大* /
if (b[i]>*p1) {*p1=b[i]; *p2=i;}
}
運(yùn)行程序:
6.4.4 指針與字符數(shù)組
在前面的課程中,我們用過(guò)了字符數(shù)組,即通過(guò)數(shù)組名來(lái)表示字符串,數(shù)組名就是數(shù)組的首地址,是字符串的起始地址。下面的例子用于簡(jiǎn)單字符串的輸入和輸出。
#include<stdio.h>
main()
{
char str[20];
gets(str);
printf(“%sn”,str);
}
現(xiàn)在,我們將字符數(shù)組的名賦予一個(gè)指向字符類型的指針變量,讓字符類型指針指向字
符串在內(nèi)存的首地址,對(duì)字符串的表示就可以用指針實(shí)現(xiàn)。其定義的方法為:charstr[20],
*P=str;這樣一來(lái),字符串str就可以用指針變量P來(lái)表示了。
#include<stdio.h>
main()
{
char str[20],*p=str;/*p=str則表示將字符數(shù)組的首地址傳遞給指針變量p*/
gets(str);
printf(“%sn”,p);
}
RUN
good morning!
goodmorning!
需要說(shuō)明的是,字符數(shù)組與字符串是有區(qū)別的,字符串是字符數(shù)組的一種特殊形式,存儲(chǔ)時(shí)以“