《C語(yǔ)言程序設(shè)計(jì)》課件2第7章_第1頁(yè)
《C語(yǔ)言程序設(shè)計(jì)》課件2第7章_第2頁(yè)
《C語(yǔ)言程序設(shè)計(jì)》課件2第7章_第3頁(yè)
《C語(yǔ)言程序設(shè)計(jì)》課件2第7章_第4頁(yè)
《C語(yǔ)言程序設(shè)計(jì)》課件2第7章_第5頁(yè)
已閱讀5頁(yè),還剩169頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第7章指針7.1地址、指針和指針變量的概念7.2指針變量的定義、引用和初始化7.3指針的運(yùn)算7.4用指針訪問(wèn)一維數(shù)組7.5*用指針訪問(wèn)二維數(shù)組7.6用指針處理字符串7.7指針數(shù)組與二級(jí)指針7.8程序舉例習(xí)題7

本章學(xué)習(xí)要求:

1.理解指針、指針變量的概念,掌握指針變量的定義與初始化,掌握指針的加減運(yùn)算和賦值運(yùn)算,了解指針的關(guān)系運(yùn)算。

2.掌握指針訪問(wèn)一維數(shù)組和二維數(shù)組,掌握指針處理字符串。

3.了解數(shù)組指針、指針數(shù)組和指向指針的指針。

指針是C語(yǔ)言中的一個(gè)重要概念,也是C語(yǔ)言的一個(gè)重要特色。指針在C語(yǔ)言中應(yīng)用極為廣泛,利用指針可以直接而快速地處理內(nèi)存中各種數(shù)據(jù)結(jié)構(gòu)的數(shù)據(jù),特別是數(shù)組、字符串、內(nèi)存的動(dòng)態(tài)分配等,它為函數(shù)間各類(lèi)數(shù)據(jù)的傳遞提供了簡(jiǎn)捷便利的方法。指針使C程序簡(jiǎn)潔、緊湊、高效。每一個(gè)學(xué)習(xí)和使用C語(yǔ)言的人,都應(yīng)當(dāng)深入地學(xué)習(xí)和掌握指針??梢哉f(shuō),不掌握指針就是沒(méi)有掌握C語(yǔ)言的精華。

指針的概念比較復(fù)雜,但使用也較靈活,而使用上的靈活性容易導(dǎo)致指針濫用而使程序失控。因此,必須全面正確地掌握指針的概念和使用特點(diǎn)。

7.1.1內(nèi)存地址

內(nèi)存地址就是內(nèi)存中存儲(chǔ)單元的編號(hào)。

計(jì)算機(jī)硬件系統(tǒng)的內(nèi)存儲(chǔ)器中,擁有大量的存儲(chǔ)單元。一般把存儲(chǔ)器中的若干個(gè)字節(jié)稱(chēng)為一個(gè)存儲(chǔ)單元。為了方便管理,必須為每一個(gè)存儲(chǔ)單元編號(hào),這個(gè)編號(hào)就是存儲(chǔ)單元的地址。每個(gè)內(nèi)存單元都有一個(gè)惟一的地址,根據(jù)一個(gè)內(nèi)存單元的地址即可準(zhǔn)確地找到該內(nèi)存單元。7.1地址、指針和指針變量的概念計(jì)算機(jī)中所有的數(shù)據(jù)都是存放在內(nèi)存單元中的,不同的數(shù)據(jù)類(lèi)型所占用的存儲(chǔ)單元大小不等,例如,整型量通常占2個(gè)字節(jié),字符量占1個(gè)字節(jié)等。要注意,內(nèi)存單元的地址與內(nèi)存單元中的數(shù)據(jù)是兩個(gè)完全不同的概念。7.1.2變量地址

如果在程序中定義了一個(gè)變量,在編譯時(shí)就給這個(gè)變量分配內(nèi)存單元。也就是說(shuō),變量實(shí)質(zhì)上代表了“內(nèi)存中某段存儲(chǔ)空間”,不同數(shù)據(jù)類(lèi)型的變量在內(nèi)存中占據(jù)空間的字節(jié)數(shù)是不一樣的,而每個(gè)變量的地址是指該變量所占用內(nèi)存空間的第一個(gè)字節(jié)的地址。如圖7.1所示,int型變量i被分配2000~2001的2個(gè)字節(jié)的存儲(chǔ)空間,則i的地址為2000。程序中對(duì)變量存取操作,就是對(duì)該變量所占用空間中存放的數(shù)據(jù)進(jìn)行操作。這種直接按變量的地址存取變量值的方式稱(chēng)為“直接存取”方式。

圖7.1變量地址與變量值的關(guān)系請(qǐng)務(wù)必弄清楚變量的地址與變量值(變量中存放的內(nèi)容)這兩個(gè)概念的區(qū)別,假設(shè)有如下程序:

main()

{inti=10,j=78,k;

k=i+j;

printf("sum=%d\n",k);

}

C程序編譯到變量定義語(yǔ)句時(shí),會(huì)為變量分配存儲(chǔ)單元。如圖7.1所示,假設(shè)系統(tǒng)分配2000和2001兩個(gè)存儲(chǔ)單元給變量i,2002和2003字節(jié)給j,2004和2005給k。在程序中一般是通過(guò)變量名來(lái)對(duì)內(nèi)存單元進(jìn)行存取操作的。其實(shí)程序經(jīng)過(guò)編譯以后已經(jīng)將變量名轉(zhuǎn)換為變量的地址,對(duì)變量值的存取都是通過(guò)地址進(jìn)行的。7.1.3指針和指針變量

1.指針

指針就是地址,是一種數(shù)據(jù)類(lèi)型。變量在內(nèi)存單元的首地址稱(chēng)為該變量的“指針”,通過(guò)變量的指針可以找到該變量所占用的內(nèi)存單元。也就是說(shuō),變量的指針指向該變量的內(nèi)存單元。

2.指針變量

指針變量,是專(zhuān)門(mén)用來(lái)存放內(nèi)存地址的變量。它是一種特殊的變量,特殊之處就在于它的變量值是地址(即指針),而不是普通的數(shù)據(jù)。

如圖7.1所示,定義一個(gè)指針變量i_pointer,用來(lái)存放整型變量的地址,編譯后該指針變量被分配兩個(gè)字節(jié),對(duì)應(yīng)的地址編碼為3010、3011??梢酝ㄟ^(guò)下面語(yǔ)句將整型變量i的地址(2000)存放到指針變量i_pointer中。

i_pointer=&i;

這時(shí),i_pointer的值就是2000,即變量i所占用存儲(chǔ)空間的起始地址。通過(guò)指針變量來(lái)訪問(wèn)其他變量的值的方式,稱(chēng)為“間接訪問(wèn)方式”。例如要存取整型變量i的值,可以先找到存放“i的地址”的指針變量i_pointer,從中取出i的地址(2000),然后到2000、2001字節(jié)取出i的值(10)。

指針變量的特點(diǎn)如下:

(1)指針變量是變量,和其他變量一樣,在內(nèi)存中占一定的存儲(chǔ)單元。

(2)指針變量存放的是其他變量的地址(指針)。

例如,指針變量i_pointer中存放的2000是指針,是變量i的指針(地址),而指針變量i_pointer自身的地址為3010。

指針變量是存放地址的變量,指針是某數(shù)據(jù)對(duì)象的地址。

7.2.1指針變量的定義

在C語(yǔ)言中,規(guī)定所有變量在使用前必須先定義后使用,指針變量也不例外,在引用指針變量之前必須先定義。指針變量的定義形式如下:

基類(lèi)型*指針變量名;

其中,*

表示這是一個(gè)指針類(lèi)型的變量,基類(lèi)型表示本指針變量所指向變量的數(shù)據(jù)類(lèi)型(即本指針變量中存放的是什么數(shù)據(jù)類(lèi)型變量的地址)。7.2指針變量的定義、引用和初始化例如:

int*p1;

即定義p1是一個(gè)指向整型變量的指針變量。至于p1究竟指向哪一個(gè)整型變量,應(yīng)由向p1賦予的地址來(lái)決定。指針變量賦值后,程序可以通過(guò)p1間接訪問(wèn)所指向的變量。

再如:

int*p2,*p3;/*定義p2、p3是指向整型變量的指針變量*/

float*p5;

/*定義p5是指向浮點(diǎn)型變量的指針變量*/

char*p4;

/*定義p4是指向字符型變量的指針變量*/在定義指針變量時(shí)要注意兩點(diǎn):

(1)指針是一種數(shù)據(jù)類(lèi)型,所以指針變量前面的“*”意思是“指向”,表示該變量為指針型變量。注意:指針變量名是p2、p3,而不是

*p2、*p3,這是與以前所介紹的定義變量的形式所不同的。指針變量中的值是某個(gè)數(shù)據(jù)對(duì)象的地址,只允許取正整數(shù),但不要把它同整型變量混淆。

(2)在定義指針變量時(shí)必須指定基類(lèi)型。我們知道,整型數(shù)據(jù)和實(shí)型數(shù)據(jù)在內(nèi)存中所占的字節(jié)數(shù)是不相同的(前者為2字節(jié),后者為4字節(jié))。一個(gè)指針變量只能指向同一個(gè)類(lèi)型的變量,而不能先指向一個(gè)整型變量,再指向一個(gè)實(shí)型變量。上面的定義中,表示p2、p3只能指向整型變量。7.2.2指針變量的引用和初始化

指針變量同普通變量一樣,使用之前不僅要定義說(shuō)明,而且必須賦予具體的值。未經(jīng)賦值的指針變量不能使用,否則將造成系統(tǒng)混亂。指針變量的值只能是地址,決不能賦予任何其他數(shù)據(jù),否則將引起錯(cuò)誤。

1.指針變量最基本的運(yùn)算符

1)取地址運(yùn)算符&

運(yùn)算符&的作用是取得變量的地址,即變量所占用內(nèi)存單元的首地址。&運(yùn)算符為單目運(yùn)算符,結(jié)合方向?yàn)橛医Y(jié)合。&運(yùn)算符后只能是一個(gè)具體的變量或數(shù)組元素,不能是表達(dá)式。例如:

int*p,m=5,s[5];

p=&m;

/*變量m的地址賦給指針變量p,使p指向變量m*/

p=&s[2];

/*數(shù)組元素s[2]的地址賦給指針變量p,使p指向 s[2]*/

2)指針運(yùn)算符*

運(yùn)算符

*

又可稱(chēng)做間接訪問(wèn)運(yùn)算符,或取值運(yùn)算符,其功能是通過(guò)指針變量間接地訪問(wèn)它所指向的變量。具體地講,間接運(yùn)算符

*

的作用是取出其后隨地址所指向變量的值。

*運(yùn)算符為單目運(yùn)算符,結(jié)合方向?yàn)橛医Y(jié)合。圖7.2指針變量的引用

例7.1

通過(guò)指針變量訪問(wèn)整型變量。

main()

{inta=100,b,*p; /*定義整型變量a、b和指針變量p*/

p=&a; /*變量a的地址賦給指針變量p*/

printf("%d,%d\n",a,*p);

*p=1000; /*整數(shù)1000賦給指針變量p所指向的變 量*/

printf("%d,%d\n",a,*p);

}

程序的運(yùn)行結(jié)果為:

100,100

1000,1000對(duì)程序的說(shuō)明:

(1)在開(kāi)頭處雖然定義了基類(lèi)型為整型的指針變量p,但它并未指向任何一個(gè)整型變量,只是規(guī)定它們可以指向整型變量。至于指向哪一個(gè)整型變量,要在程序語(yǔ)句中指定。程序第3行的作用就是使p指向a,見(jiàn)圖7.2。此時(shí)p的值為

&a(即a的地址)。

(2)第4行與第6行的

*p就是取變量a的值。

(3)要區(qū)分好定義指針變量時(shí)的

*

與引用、賦值時(shí)的

*

作用是不相同的。定義處的

*

只代表所定義的變量為指針類(lèi)型變量,而引用、賦值時(shí)的

*

則表明取指針變量所指向的存儲(chǔ)單元的值。如上例第一行語(yǔ)句中,p*

表明定義了一個(gè)指針變量,而隨后的

*p則代表p所指向的變量a,所以第一個(gè)printf語(yǔ)句的輸出結(jié)果為100,100。語(yǔ)句

*p=1000;表明把整數(shù)1000賦值給指針變量所指向的變量a,所以該語(yǔ)句等價(jià)于a

=

1000。因此,第二個(gè)printf語(yǔ)句的輸出結(jié)果為1000,1000。

(4)第3行“p=&a;”是將a的地址賦給指針變量p。注意不應(yīng)寫(xiě)成“*p=&a;”。因?yàn)閍的地址是賦給指針變量p,而不是賦給*p(即p所指向的存儲(chǔ)單元)。請(qǐng)對(duì)照?qǐng)D7.2進(jìn)行分析。對(duì)“&”和“*”運(yùn)算符再作如下說(shuō)明:

(1)如果已執(zhí)行了“p1=&a;”語(yǔ)句,若有

&*p1,它的含義是什么?“&”和“*”兩個(gè)運(yùn)算符的優(yōu)先級(jí)別相同,但按自右而左的方向結(jié)合(右結(jié)合性),因此先進(jìn)行

*p1的運(yùn)算,它就是變量a,再執(zhí)行&運(yùn)算。因此,&*p1與

&a相同,即變量a的地址。

如果有“p2=&*p1;”,它等效于“p2=&a;”。它的作用是將

&a(a的地址)賦給p2,如果p2原來(lái)指向b,經(jīng)過(guò)重新賦值后它已不再指向b,而是指向a。如圖7.3所示,圖7.3(a)是原來(lái)的情況,圖7.3(b)是執(zhí)行上述賦值語(yǔ)句后的情況。

圖7.3指針變量與其指向的目標(biāo)變量的關(guān)系

(2)

*&a的含義。先進(jìn)行&a運(yùn)算,得到a的地址,再進(jìn)行*

運(yùn)算。即取

&a所指向變量的值,*&a和

*p1的作用是一樣的(假設(shè)已執(zhí)行了“p1=&a”),它們等價(jià)于變量a。即

*&a與a等價(jià),見(jiàn)圖7.4。

(3)

(*p1)++

相當(dāng)于a++。假設(shè)已執(zhí)行了“p1=&a”,則

(*p1)++

中括號(hào)的有無(wú)是不同的。如果沒(méi)有括號(hào),就成為了

*p1++。++

*

為同一優(yōu)先級(jí)別,而結(jié)合方向?yàn)樽杂叶?右結(jié)合性),因此它相當(dāng)于

*(p1++)。由于++在p1的右側(cè),是“后加”,因此先對(duì)p1的原值進(jìn)行*運(yùn)算,得到a的值,然后使p1的值改變,這樣p1就不再指向a。而

(*p1)++

中,p1的值不改變,改變的是變量a的值,p1仍然指向a。

圖7.4*&a的含義下面舉一個(gè)指針變量應(yīng)用的例子。

例7.2

輸入a和b兩個(gè)整數(shù),按先大后小的順序輸出a和b。

main()

{int*p1,*p2,*p,a,b;

scanf("%d,%d",&a,&b);

p1=&a;p2=&b;

if(a<b)

{p=p1;p1=p2;p2=p;}

printf("a=%d,b=%d\n",a,b);

printf("max=%d,min=%d\n",*p1,*p2);

}

運(yùn)行情況如下:

6,9↙

a=6,b=9

max=9,min=6

注意,本題變量a和b的值并未交換,它們?nèi)员3衷?,但p1和p2的值改變了。p1的值原為

&a,后來(lái)變成

&b;p2原值為

&b,后來(lái)變成

&a。這樣在輸出

*p1和

*p2時(shí),實(shí)際上是輸出變量b和a的值,所以先輸出9,然后輸出6。(注意:*p1代表p1所指向的變量,而p1為指針變量。)

指針變量值的交換情況見(jiàn)圖7.5(圖7.5(a)為交換前的情況,圖7.5(b)為交換后的情況)。

圖7.5指針變量值的交換所產(chǎn)生的指向變化這個(gè)問(wèn)題的算法是不交換整型變量的值,而是交換兩個(gè)指針變量的值(即a和b的地址)。

2.指針變量的初始化

在定義指針變量的同時(shí)給指針變量賦初值,叫做指針變量的初始化。一般形式如下:

類(lèi)型說(shuō)明符*指針變量名=初始地址值;

例如:

inta;

int*p=&a;

/*定義p為一個(gè)指針變量,且指向變量a*/注意,任何指針變量在使用之前都要進(jìn)行定義并賦值,未經(jīng)賦值的指針變量是禁止使用的;在給指針初始化時(shí),要注意類(lèi)型匹配的問(wèn)題,也就是說(shuō),必須是同一類(lèi)型數(shù)據(jù)的地址進(jìn)行指針初始化;可以把一個(gè)指針的值賦給另一個(gè)指針,但不能直接用整型數(shù)據(jù)賦值給指針變量,例如:int*p=1000是錯(cuò)誤的,應(yīng)注意避免這樣的賦值。

例7.3

分析下面的程序,指出其中的錯(cuò)誤。

main()

{int*p1,*p2,a=5;

floatb=10.5;

*p1=a;

p2=&b;

printf("%d,%d\n",(*p1),(*p2));

}本程序的錯(cuò)誤共有以下兩處:

(1)第1個(gè)錯(cuò)誤是使用了未賦值的指針變量。程序的第2行定義p1為指針變量,但并沒(méi)有賦值。而語(yǔ)句

*p1=a的功能是給p1所指向的變量賦值,但p1未指向任何變量,這容易引起系統(tǒng)的混亂,是不允許的。

(2)第2個(gè)錯(cuò)誤是“類(lèi)型不匹配”問(wèn)題,即指針變量的類(lèi)型與其指向變量的類(lèi)型不相同。程序中定義p2是指向int型的指針變量,語(yǔ)句p2=&b;將浮點(diǎn)型變量b的地址賦給指向int型的指針變量p2,出現(xiàn)了類(lèi)型不匹配的錯(cuò)誤。

在C語(yǔ)言中,指針可以進(jìn)行某些運(yùn)算,但其運(yùn)算的種類(lèi)是有限的,它只能進(jìn)行賦值運(yùn)算、部分算術(shù)運(yùn)算及關(guān)系運(yùn)算。7.3指?針?的?運(yùn)?算7.3.1賦值運(yùn)算

指針變量的賦值運(yùn)算只能在同一數(shù)據(jù)類(lèi)型之間進(jìn)行,有以下幾種形式。

int*p1,*p2,a,s[5];

int*p1=&a; /*指針變量初始化賦值*/

p2=&a; /*把變量a的地址賦給指針變量p2*/

p1=p2; /*p1和p2都是指針變量,把p2的值賦給p1*/

p1=s; /*把數(shù)組s的首地址賦給指針變量p1*/

p1=&s[3]; /*把數(shù)組元素s[3]的地址賦給指針變量p1*/

p1=NULL; /*把空指針NULL賦給指針變量p1*/

NULL是一個(gè)符號(hào)常量,代表整數(shù)0,在stdio.h頭文件中有對(duì)其的定義。將指針變量賦值為NULL,表示該指針變量當(dāng)前未指向任何變量,其值為0。

對(duì)指針變量賦0值和不賦值是不同的。指針變量未賦值時(shí),程序編譯時(shí)分配給指針變量的存儲(chǔ)空間中的值是不確定的,可以是任意值,直接使用該指針變量可能造成意外錯(cuò)誤。而指針變量賦NULL值后,值是確定的,則可以使用,只是它不指向具體的變量而已。

例7.4指針變量的賦值運(yùn)算。

main()

{inta=10,b=20,s,t,x[5]={1,2,3,4,5},*p1,*p2;

p1=&a;

p2=&b;

s=*p1+*p2;

t=*p1**p2;

printf("a=%d,b=%d,a+b=%d,a*b=%d\n",a,b,s,t);

p1=x;

p2=&x[2];

printf("%d,%d\n",*p1,*p2);

}程序運(yùn)行結(jié)果:

a=10,b=20,a+b=30,a*b=200

1,3

執(zhí)行語(yǔ)句t=*p1**p2;

時(shí),*

為單目運(yùn)算符,具有右結(jié)合性,其優(yōu)先級(jí)高于乘法運(yùn)算符,因此先計(jì)算

*p1和*p2,然后再進(jìn)行乘法運(yùn)算。

注意:不能直接將整數(shù)賦給指針變量,例如賦值語(yǔ)句p1=1000是錯(cuò)誤的。同樣也不應(yīng)該把指針變量p1的值(地址)賦給一個(gè)整型變量,例如a=p1是錯(cuò)誤的。7.3.2算術(shù)運(yùn)算

指針的算術(shù)運(yùn)算是按地址計(jì)算規(guī)則進(jìn)行的,所以指針的算術(shù)運(yùn)算應(yīng)考慮到指針?biāo)赶蜃兞康臄?shù)據(jù)類(lèi)型。指針的算術(shù)運(yùn)算主要包括以下幾種類(lèi)型。

1.指針與整數(shù)的加、減運(yùn)算

對(duì)于指向數(shù)組的指針變量,可以加上或減去一個(gè)整數(shù)n。例如:假設(shè)p是指向數(shù)組a的指針變量,則p+n、p-n、p++、++p、p--、--p運(yùn)算都是合法的(只要不超過(guò)數(shù)組范圍)。

指針變量加或減一個(gè)整數(shù)n的意義是,把指針指向的當(dāng)前位置(指向某數(shù)組元素)向前或向后移動(dòng)n個(gè)位置。例如,假設(shè)指針變量p當(dāng)前指向數(shù)組元素a[0],則p+n指向數(shù)組元素a[n]。例如:

inta[5],*p;

p=a;/*p指向數(shù)組a,即指向數(shù)組元素a[0]*/

p++;/*p指向下一個(gè)數(shù)組元素,即指向a[1]*/

p=a;/*重新將數(shù)組首地址賦值給p,即p指向數(shù)組元素a[0]*/

p=p+2;/*使p的當(dāng)前指向向后移動(dòng)2個(gè)位置,即p指向數(shù)組元素 a[2]*/

注意,指針變量的加減運(yùn)算只能對(duì)數(shù)組指針變量進(jìn)行,對(duì)指向其他類(lèi)型變量的指針變量進(jìn)行加減運(yùn)算是毫無(wú)意義的。

例7.5

分析下面程序的運(yùn)行結(jié)果。

main()

{inta[10]={0,1,2,3,4,5,6,7,8,9},x,y,*p;

p=&a[0];

printf("%d%d%d\n",*p,*(p+2),*(p+5));

x=*p++; /*等價(jià)于*(p++)*/

p=&a[0];

y=*++p; /*等價(jià)于*(++p)*/

printf("%d%d\n",x,y);

}程序運(yùn)行結(jié)果:

025

01

指針的加減算術(shù)運(yùn)算說(shuō)明:

(1)兩個(gè)指針變量之間的運(yùn)算,只有指向同一數(shù)組的兩個(gè)指針變量之間才能進(jìn)行,否則運(yùn)算毫無(wú)意義。

(2)

*p++等價(jià)于*(p++),表示先取p所指元素的值,再把指針變量加1,即指向當(dāng)前元素的后一個(gè)元素。

(3)

*++p等價(jià)于*(++p),表示先把指針變量加1,然后再取所指元素的值。

2.兩個(gè)指針相減

兩個(gè)指針可以相減,但主要用于同類(lèi)型的指針變量,并且兩個(gè)指針變量指向同一個(gè)數(shù)組中的數(shù)組元素。

兩指針變量相減所得之差是兩個(gè)指針?biāo)笖?shù)組元素之間相差的存儲(chǔ)單元個(gè)數(shù),即表示兩個(gè)指針?biāo)赶虻臄?shù)組元素之間的位置關(guān)系。例如:假設(shè)p1指向a[1],p2指向a[4],則p2-p1的結(jié)果為3,表示a[4]的存儲(chǔ)位置在a[1]向后偏移3個(gè)單元。

指針相減在字符串操作中應(yīng)用較多。

注意:兩個(gè)指針變量不能進(jìn)行加法運(yùn)算,例如p1

+

p2是毫無(wú)實(shí)際意義的。7.3.3關(guān)系運(yùn)算

指針之間的關(guān)系運(yùn)算可確定它們所指向的數(shù)組對(duì)象存儲(chǔ)位置的前后關(guān)系,所進(jìn)行的比較是兩個(gè)指針變量所指向存儲(chǔ)單元地址的比較。

通常,只有進(jìn)行關(guān)系運(yùn)算的兩個(gè)指針指向同一組數(shù)組時(shí),方可表示它們所指數(shù)組元素之間的關(guān)系,這樣比較才有意義。指向前面元素的指針變量“小于”指向后面元素的指針變量。假設(shè)p1和p2指向同一數(shù)組a,并有p1=&a[1];p2=&a[5],則

p1==p2 結(jié)果為假,只有當(dāng)p1和p2指向同一數(shù)組元素時(shí)才為真;

p1<p2 結(jié)果為真,當(dāng)p1指向的變量在p2指向的變量之前時(shí)為真;

p1<=p2 結(jié)果為真,當(dāng)p1指向的變量在p2指向的變量之前或相同時(shí)為真;

p1>p2 結(jié)果為假,當(dāng)p1指向的變量在p2指向的變量之后時(shí)為真;

p1>=p2 結(jié)果為假,當(dāng)p1指向的變量在p2指向的變量之后或相同時(shí)為真;

p1!=p2 結(jié)果為真,當(dāng)p1指向的變量和p2指向的變量位置不同時(shí)為真。此外,指針變量還可以與NULL(0)比較。設(shè)p為指針變量且p=NULL,則p==0結(jié)果為真,表明p是空指針,它不指向任何變量。

一個(gè)數(shù)組占用一塊連續(xù)的內(nèi)存單元,包含若干元素,每個(gè)數(shù)組元素都在內(nèi)存中占用存儲(chǔ)單元,它們都有相應(yīng)的地址。每個(gè)數(shù)組元素按其類(lèi)型不同占有數(shù)目不同的幾個(gè)連續(xù)的內(nèi)存單元,一個(gè)數(shù)組元素的地址就是它所占有的若干內(nèi)存單元的首地址。

C語(yǔ)言規(guī)定,數(shù)組名代表數(shù)組的首地址,也就是0號(hào)元素的地址。一維數(shù)組中,第一個(gè)元素的地址,即該數(shù)組的起始地址。7.4用指針訪問(wèn)一維數(shù)組7.4.1建立指針變量與一維數(shù)組的聯(lián)系

例如,要?jiǎng)?chuàng)建一個(gè)指向某個(gè)int型一維數(shù)組a的指針變量pa,可先定義:

inta[10],*pa;

然后對(duì)指針變量賦值:

pa=a;或pa=&a[0];

由于數(shù)組名a代表該數(shù)組的首地址,也即a[0]的地址,因此指針變量pa指向該數(shù)組首地址,此時(shí),pa、a、&a[0]均指向同一內(nèi)存單元,如圖7.6所示。

圖7.6指針變量與數(shù)組建立關(guān)聯(lián)注意,數(shù)組名a不代表整個(gè)數(shù)組,上述“pa=a;”的作用是“把數(shù)組a的首地址賦給指針變量pa”,而不是“把數(shù)組a各元素的值賦給p”。

也可在定義指針變量時(shí)直接對(duì)其初始化,如:

inta[10],*pa=a;

特別情況下,也可以在指針變量定義后使指針變量指向某一個(gè)數(shù)組元素,例如:

pa=&a[5];

此時(shí)*pa的值就是數(shù)組元素a[5]的值。7.4.2用指針訪問(wèn)數(shù)組元素

按C語(yǔ)言的規(guī)定:如果指針變量p已指向數(shù)組中的一個(gè)元素,則p+1指向同一數(shù)組中的下一個(gè)元素(而不是將p值簡(jiǎn)單地加1)。例如,數(shù)組元素是實(shí)型,每個(gè)元素占4個(gè)字節(jié),則p+1意味著使p的值(地址)加4個(gè)字節(jié),以使它指向下一元素。p+1所代表的地址實(shí)際上是p+1×d,d是一個(gè)數(shù)組元素所占的字節(jié)數(shù)(對(duì)整型,d=2;對(duì)實(shí)型,d=4;對(duì)字符型,d=1)。實(shí)際上,在編譯時(shí),對(duì)數(shù)組元素a[i]就是處理成*(a+i),即按數(shù)組首地址加上相對(duì)位移量得到要找的元素的地址,然后找出該單元中的內(nèi)容,也即a[i]與*(a+i)無(wú)條件等價(jià)。例如,若數(shù)組a的首地址為1000,設(shè)數(shù)組為整型,則a[3]的地址是這樣計(jì)算出來(lái)的:1000+3×2=1006,然后從1006地址所標(biāo)志的整型單元取出元素的值,即a[3]的值。

使用指針的目的是為了處理指針指向的目標(biāo)數(shù)據(jù)。一旦指針和數(shù)組建立了聯(lián)系,就可以通過(guò)指針來(lái)引用數(shù)組元素。通常引用一個(gè)數(shù)組元素,有以下三種方法:

(1)下標(biāo)法,如a[i]形式。

(2)數(shù)組名地址法。由于數(shù)組名是數(shù)組的首地址,因而a

+

i就表示以數(shù)組名a為起始地址的順數(shù)第i個(gè)元素即a[i]的地址,那么*(a+i)即為a[i]。

(3)指針?lè)ǎ灿腥缦聝煞N形式。

①指針地址法。假設(shè)已執(zhí)行語(yǔ)句“pa=a;”,則pa+i就表示以pa為起始地址順數(shù)第i個(gè)元素即a[i]的地址,那么*(pa+i)即為a[i]。

②指針下標(biāo)法。假設(shè)已執(zhí)行語(yǔ)句“pa=a;”,由指針地址法知*(pa+i)相當(dāng)于a[i];此外C語(yǔ)言還允許直接用pa[i]的形式來(lái)表示以pa指示的位置為起點(diǎn)順數(shù)第i個(gè)同類(lèi)型的數(shù)據(jù)。綜上所述,對(duì)同一數(shù)據(jù)類(lèi)型指針pa和數(shù)組a來(lái)說(shuō),一旦二者建立了pa

=

a的聯(lián)系(即使pa指向數(shù)組a的首地址),則下述對(duì)數(shù)組元素a[i]的表示方式就是等價(jià)的:

a[i]、*(a+i)、*(pa+i)、pa[i]

此外,還可以用動(dòng)態(tài)改變的指針變量指向不同的數(shù)組元素,常見(jiàn)的方法是在循環(huán)中使用pa++的方式來(lái)依次指向連續(xù)的各個(gè)數(shù)組元素。

例7.6數(shù)組元素的等價(jià)引用。

#include<stdio.h>

main()

{int*p,num[4],i;

p=num; /*建立指針和數(shù)組的關(guān)聯(lián)*/

for(i=0;i<4;i++)

*(p+i)=i+1; /*用指針地址法引用數(shù)組元素*/

for(i=0;i<4;i++)

printf("num[%d]=%d\t",i,*(num+i)); /*用數(shù)組名地址法引用 數(shù)組元素*/

printf("\n");

for(p=num;p<(num+4);p++)

printf("%3d",*p); /*用動(dòng)態(tài)變化的指針變量引用數(shù)組元 素*/

}

運(yùn)算情況如下:

num[0]=1num[1]=2num[2]=3num[3]=4

1234

例7.7分析下面程序的運(yùn)行結(jié)果。

main()

{inta[]={1,2,3,4,5},y,*p;

p=&a[2];

printf("%d",*p);

p=a+2;printf("%d",*++p);

p=a+2;printf("%d",*p++);

p=a+2;printf("%d",(*++p));

p=a+2;printf("%d",(*p)++);

printf("\n");

}程序運(yùn)行結(jié)果:

3

4

3

4

3

在上例中,要注意以下幾點(diǎn):

(1)

*++p與(*++p)的作用完全相同,均是先使指針變量p自增,然后再取其所指向的目標(biāo)變量,即a[3]。

(2)

*p++等價(jià)于*(p++),而由于++運(yùn)算符在p之后,則先用p值,然后再使p值自增。因此,*p++先得到a[2]值,然后p指向a[3]。

(3)(*p)++表示先使用p所指向的元素,即a[2],然后再使該元素自增,即a[2]的值變?yōu)?。注意,此時(shí)*p等價(jià)于a[2]。由上述例題可知,訪問(wèn)數(shù)組元素的方法有多種,如何根據(jù)需要來(lái)分別使用呢?一般來(lái)說(shuō),如果希望程序嚴(yán)格按照遞增或遞減順序訪問(wèn)數(shù)組,則用指針移動(dòng)的方法來(lái)順序處理顯得快捷方便;但若對(duì)數(shù)組的訪問(wèn)是隨機(jī)方式,比如只訪問(wèn)其中的一個(gè)或幾個(gè)元素,則以下標(biāo)定位的方法更簡(jiǎn)潔明了。另外,用下標(biāo)法比較直觀,用地址法或指針變量的方法不直觀,難以很快地判斷出當(dāng)前處理的是哪一個(gè)元素,有時(shí)還需仔細(xì)分析指針變量p的當(dāng)前指向,才能判斷當(dāng)前輸出的是第幾個(gè)元素,比如例7.7。使用指針訪問(wèn)數(shù)組元素時(shí),還要注意以下事項(xiàng):

(1)指針變量是變量,其值可以改變;而數(shù)組名是常量,值不能改變。例如,設(shè)p為指針變量,a為數(shù)組名,b為int型變量,則p++是合法的,而a++、a=p、a=&b都是錯(cuò)誤的,因?yàn)槌A坎荒鼙毁x值。

(2)要注意指針變量的當(dāng)前值。雖然定義數(shù)組時(shí)指定它包含10個(gè)元素,但指針變量可以指到數(shù)組以后的內(nèi)存單元,系統(tǒng)并不認(rèn)為非法。例如:

inta[10],*p=a;

*(p+20)=50;上面的語(yǔ)句給a[20]賦值,明顯越界,但系統(tǒng)并不認(rèn)為是錯(cuò)誤的。因?yàn)樵贑語(yǔ)言中,無(wú)數(shù)組下標(biāo)越界錯(cuò)誤,即C不檢查數(shù)組下標(biāo)是否越界。這樣容易引起系統(tǒng)的混亂,所以使用指針編寫(xiě)程序時(shí)要注意不要越界。

例7.8

通過(guò)指針變量輸出數(shù)組a的10個(gè)元素。

main()

{int*p,i,a[10];

p=a;

for(i=0;i<10;i++)

scanf("%d",p++);

printf("\n");

for(i=0;i<10;i++,p++)

printf("%d",*p);

printf("\n");

}這個(gè)程序乍看起來(lái)好像沒(méi)有什么問(wèn)題,其運(yùn)行情況如下:

1234567890

-222851-242314-200149192159621077

顯然輸出的數(shù)值并不是數(shù)組a中各元素的值。

原因是指針變量的初始值為數(shù)組a首地址(見(jiàn)圖7.7中的①),但經(jīng)過(guò)第一個(gè)for循環(huán)讀入數(shù)據(jù)后,p已指向數(shù)組a的末尾(見(jiàn)圖7.7中的②)。因此,在執(zhí)行第二個(gè)for循環(huán)時(shí),p的起始值不是&a[0],而是a+10。則在第二個(gè)循環(huán)中,每次執(zhí)行p++,結(jié)果p指向的是數(shù)組a下面的10個(gè)元素,而這些存儲(chǔ)單元中的值是不可預(yù)料的。解決這個(gè)問(wèn)題的辦法是,只要在第二個(gè)for循環(huán)之前增加一個(gè)賦值語(yǔ)句:

p=a;

for(i=0,p=a;i<10;i++,p++)

使p的初始值回到

&a[0],這樣結(jié)果就正確了。

圖7.7變化的指針與指向的變化

前已述及,利用指針可指向一維數(shù)組,并可通過(guò)指針引用數(shù)組元素。利用指針也可以指向多維數(shù)組,本節(jié)主要介紹利用指針指向二維數(shù)組。利用指針引用二維數(shù)組元素,比引用一維數(shù)組元素復(fù)雜,有些表示方法較難理解,為了更好地理解指向二維數(shù)組的指針,更好地通過(guò)指針引用二維數(shù)組元素,必須進(jìn)一步搞清二維數(shù)組的地址及元素的關(guān)系和表示方法。7.5*用指針訪問(wèn)二維數(shù)組7.5.1二維數(shù)組的地址

多維數(shù)組是按行連續(xù)存儲(chǔ)的,因此,二維數(shù)組元素也是按行連續(xù)存儲(chǔ)的,于是就可以使用指針來(lái)訪問(wèn)這些連續(xù)的存儲(chǔ)單元。

設(shè)有整型二維數(shù)組a[3][4],定義如下:

inta[3][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11}};從二維數(shù)組的角度來(lái)看,a代表整個(gè)二維數(shù)組的首地址,也就是第0行的首地址。a+

1代表第1行的首地址。如果二維數(shù)組的首地址為2000,則a

+

1為2008,因?yàn)榈?行有4個(gè)整型數(shù)據(jù),因此a

+

1的含義是a[1]的地址,即a

+

4

×

2

=

2008。a

+

2代表第2行的首地址,它的值是2016,見(jiàn)圖7.8。(注意:C語(yǔ)言中,地址值一般為16位二進(jìn)制數(shù)。為符合使用習(xí)慣,此處采用十進(jìn)制。)

圖7.8數(shù)組名地址與指向的關(guān)系

C語(yǔ)言允許把一個(gè)二維數(shù)組分解為多個(gè)一維數(shù)組來(lái)處理。因此數(shù)組a可以理解為由3個(gè)一維數(shù)組組成,即數(shù)組a由a[0]、a[1]和a[2]三個(gè)元素組成,而每個(gè)元素又含有4個(gè)元素。例如,a[0]含有a[0][0]、a[0][1]、a[0][2]和a[0][3]四個(gè)元素,a[0]為一維數(shù)組名。

a[0]、a[1]和a[2]既然是一維數(shù)組名,而C語(yǔ)言規(guī)定數(shù)組名代表數(shù)組的首地址,因此,a[0]就代表第0行一維數(shù)組中第0列元素的地址,即數(shù)組元素a[0][0]的地址,也即&a[0][0],a[1]的值是&a[1][0],a[2]的值是&a[2][0]。

a[0]

+

1代表第0行第1列元素的地址,見(jiàn)圖7.9。因a[0]是一維數(shù)組名,則“a[0]

+

1”中的1代表1個(gè)列元素的字節(jié)數(shù),即2個(gè)字節(jié),而不是代表1行8個(gè)字節(jié)。a[0]的值為2000,則a[0]

+

1的值是2002(而不是2008)。這是因?yàn)楝F(xiàn)在是在一維數(shù)組范圍內(nèi)討論問(wèn)題,正如有一個(gè)一維數(shù)組x,x

+

1是其第1列元素地址一樣。a[0]

+

0、a[0]

+

1、a[0]

+

2、a[0]

+

3分別是a[0][0]、a[0][1]、a[0][2]、a[0][3]的地址(即&a[0][0]、&a[0][1]、&a[0][2]、&a[0][3])。為了理解上的方便,我們可以定義行指針與列指針的概念。行指針為指向一維數(shù)組一整行元素的指針,列指針為指向某一個(gè)具體列元素的指針。例如,從圖7.9中可見(jiàn),a、a

+

1、

a

+

2為行指針,a[0]、a[0]

+

1、a[0]

+

2、a[0]

+

3為列指針。有時(shí)行、列指針是可以相互轉(zhuǎn)化的。在行指針前面加一個(gè)*,就轉(zhuǎn)換為列指針。例如,a和a

+

1是行指針,在它們前面加一個(gè)

*

就是

*a和

*(a

+

1),它們就成為列指針,分別指向a數(shù)組0行0列的元素和1行0列的元素。反之,在一維數(shù)組名的列指針(即每一行第0列元素的地址)前面加&,就成為行指針。例如,a[0]是指向0行0列元素的列指針,在它前面加一個(gè)&,得&a[0],由于a[0]與*(a

+

0)等價(jià),因此&a[0]與&*a等價(jià),也就是與a等價(jià),它指向二維數(shù)組的0行。同理,&a[1]、&a[2]分別等價(jià)于a

+

1、a

+

2。注意,如果在非0列元素列指針前加&,則不能轉(zhuǎn)化為行指針,僅僅是取出該元素的地址,例如,&a[0][1]是元素a[0][1]的地址,并非轉(zhuǎn)化為行指針。

圖7.9二維數(shù)組中的行指針與列指針有必要對(duì)a[i]的性質(zhì)作進(jìn)一步說(shuō)明。a[i]從形式上看是數(shù)組a中第i個(gè)元素。如果a是一維數(shù)組名,則a[i]代表數(shù)組a第i個(gè)元素所占的內(nèi)存單元。a[i]是有物理地址的,是占內(nèi)存單元的。但如果a是二維數(shù)組,則a[i]是代表一維數(shù)組名。a[i]本身并不占實(shí)際的內(nèi)存單元,它也不存放數(shù)組a中各個(gè)元素的值。它只是一個(gè)地址(如同一個(gè)一維數(shù)組名x并不占內(nèi)存單元而只代表地址一樣)。a、a

+

i、a[i]、*(a

+

i)、*(a

+

i)

+

j、a[i]

+

j都是地址。*(a[i]

+

j)、*(*(a

+?i)

+

j)是二維數(shù)組元素a[i][j]的值,見(jiàn)表7.1。

表7.1二維數(shù)組的指針、元素表示形式與含義注意,雖然表7.1中a

+

l和*(a

+

1)的值都是2008,但二者的含義是不相同的。a

+

1是行指針,指向第1行首地址,而*(a

+

1)就是a[1],是一維數(shù)組名,是指向元素a[1][0]的列指針。同理,行指針&a[i]和列指針a[i]的值是一樣的,但它們的含義是不同的。

此外,還要注意,當(dāng)a是二維數(shù)組名時(shí),a是行指針,*a是列指針,**a是0行0列元素的值。同樣,a

+

l(行指針)指向第1行首地址,但也不能用*(a+1)(列指針)得到a[1][0]的值,而應(yīng)該用**(a

+

1)求a[1][0]元素的值。7.5.2指向多維數(shù)組的指針變量

在了解上面的概念后,可以用指針變量指向多維數(shù)組及其元素。

1.指向數(shù)組元素的指針變量

例7.9

用指針變量輸出數(shù)組元素的值。

main()

{inta[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};

int*p,i;

p=a[0]; /*a[0]為列指針,使p指向a[0][0],其值為 &a[0][0]*/

for(i=0;i<12;i++)

{if(i%4==0)printf("\n");

printf("%4d",*p++); /*通過(guò)p值的變化,逐一訪問(wèn)各元素*/

}

}

運(yùn)行結(jié)果如下:

1234

5678

11101112

p是一個(gè)基類(lèi)型為整型的指針變量,它可以指向一般的整型變量,也可以指向整型的數(shù)組元素。每次使p值加1,以移向下一元素。if語(yǔ)句的作用是使一行輸出4個(gè)數(shù)據(jù),然后換行。

2.指向由m個(gè)元素組成的一維數(shù)組的指針變量

例7.9的指針變量p指向整型數(shù)組元素,p

+

1所指向的元素是p所指向元素的下一元素。可以改用另一方法輸出數(shù)組元素:使p指向一個(gè)包含m個(gè)元素的一維數(shù)組。這時(shí),p為行指針,先指向第0行(即p

=

&a[0]),然后用p

+

1指向第1行(即p

=

&a[1]),p的增值以一維數(shù)組的長(zhǎng)度為單位。

例7.10

輸出二維數(shù)組任一行任一列元素的值。

main()

{inta[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};

int(*p)[4],i,j; /*定義行指針變量p*/

p=a; /*a為行指針,指向第0行*/

for(i=0;i<3;i++)

for(j=0;j<4;j++)

printf("%2d",*(*(p+i)+j));

printf("\n");

}運(yùn)行情況如下:

1357911131517192123

程序第3行“int(*p)[4]”表示p是一個(gè)指針變量,它指向包含4個(gè)元素的一維數(shù)組。注意,p兩側(cè)的括號(hào)不可缺少,如果寫(xiě)成*p[4],由于方括號(hào)[]運(yùn)算級(jí)別高,因此p先與[4]結(jié)合,是數(shù)組,然后再與前面的*結(jié)合,*p[4]是指針數(shù)組。為便于理解,可以對(duì)下面二者進(jìn)行比較:

(1)

inta[4];(a有4個(gè)元素,每個(gè)元素為整型)

(2)

int(*p)[4];

第(2)種形式表示*p有4個(gè)元素,每個(gè)元素為整型。也就是p所指的對(duì)象是有4個(gè)整型元素的數(shù)組,即p是行指針,見(jiàn)圖7.10(a)。應(yīng)該記住,此時(shí)p只能指向一個(gè)包含4個(gè)元素的一維數(shù)組,p的值就是該一維數(shù)組的首地址。p只能指向一整行元素,而不能指向一維數(shù)組中的某個(gè)具體元素。

圖7.10行指針變量的定義與應(yīng)用程序中的p+i是行指針,指向二維數(shù)組a的第i行,見(jiàn)圖7.10(b)。*(p

+

i)被轉(zhuǎn)化為列指針,因此,*(p

+

2)

+

3是數(shù)組a第2行第3列元素的地址,這是指向列的指針,*(*(p

+

2)

+

3)是a[2][3]的值。

注意:p+2(行指針)和*(p+2)(列指針)具有相同的值,但(p

+

2)

+

3和*(p

+

2)

+

3的值就不相同了。因?yàn)?p

+

2)

+

3等效于(p

+

5),仍為行指針,是第5行的首地址,而*(p

+

2)

+

3中的*(p

+

2)已轉(zhuǎn)化為列指針,指向第2行第0列,+

3后則指向同一行的第3列元素,即*(p

+

2)

+

3是元素a[2][3]的地址。上述結(jié)論均可推廣到三維及三維以上的數(shù)組。例如,定義了一個(gè)數(shù)組t[3][4][5],它可看成由t[0]、t[1]、t[2]三個(gè)二維數(shù)組組成,每個(gè)二維數(shù)組又由4個(gè)一維數(shù)組組成,而每個(gè)一維數(shù)組含有5個(gè)元素;其中,t[0]數(shù)組分別由t[0][0]、t[0][1]、t[0][2]、t[0][3]四個(gè)一維數(shù)組組成,其他類(lèi)推。由于t[i][j]可用*(t[i]

+

j)表示,則元素t[i][j][k]可用*(t[i][j]+k)、*(*(t[i]

+

j)

+

k)或者*(*(*(t

+

i)

+

j)

+

k)表示。

在C語(yǔ)言中,沒(méi)有專(zhuān)門(mén)存放字符串的變量,一個(gè)字符串可以存放在一個(gè)字符數(shù)組中,數(shù)組名表示該字符串第一個(gè)字符存放的地址。也可以將字符串的首地址賦給一個(gè)字符型指針變量,該指針變量便指向這個(gè)字符串。

引用字符串時(shí),可以逐個(gè)字符引用,也可以整體引用。7.6用指針處理字符串

例7.11

使用字符數(shù)組存放一個(gè)字符串,然后輸出該字符串。

main()

{charstring[]="WelcometostudyClanguages.";

inti=0;

while(string[i]!='\0')

printf("%c",string[i++]);

}

運(yùn)行時(shí)輸出:

WelcometostudyClanguages.

本例中,字符串用字符型一維數(shù)組存放,然后再逐一字符加以輸出。也可以通過(guò)格式符%s及數(shù)組名string的配合,一次性輸出全部字符。即可修改上述程序?yàn)?/p>

main()

{charstring[]="WelcometostudyClanguages.";

printf("%s",string);

}

程序中的%s表示輸出一個(gè)字符串,給出的數(shù)組名string是指向數(shù)組首地址的指針,則系統(tǒng)先輸出它所指向的第一個(gè)字符數(shù)據(jù),然后指針自動(dòng)下移,使之指向下一個(gè)字符,再輸出一個(gè)字符……

如此,直到遇到字符串結(jié)束標(biāo)志'\0'為止。注意,在內(nèi)存中,字符串的最后被自動(dòng)加了一個(gè)'\0',因此在輸出時(shí)能確定字符串的終止位置。

顯然,用%s可以對(duì)一個(gè)字符串進(jìn)行整體的輸入/輸出。

也可以不定義字符數(shù)組,而定義一個(gè)字符指針,用字符指針指向字符串中的字符。

例7.12

用字符指針指向一個(gè)字符串,然后輸出該字符串及其長(zhǎng)度。

main()

{char*string;

intlen;

string="WelcometostudyClanguages.";

printf("%s\n",string);

printf("Length=%d\n",strlen(string));

}

程序運(yùn)行結(jié)果如下:

WelcometostudyClanguages.

Length=29在這里沒(méi)有定義字符數(shù)組,在程序中定義了一個(gè)字符指針變量string,并使它指向一個(gè)字符串常量"WelcometostudyClanguages."。在C語(yǔ)言中,對(duì)字符串常量是按字符數(shù)組處理的,在內(nèi)存中開(kāi)辟一塊連續(xù)的存儲(chǔ)空間用于存放字符串常量。程序中把字符串常量賦值給string的實(shí)質(zhì)是把該字符串的首地址(即存放字符串連續(xù)存儲(chǔ)空間的首地址)賦值給指針變量string,也就是使string指向該字符串。賦值后就可以通過(guò)指針變量string來(lái)存取它所指向的字符串了。

對(duì)字符串中字符的存取,可以用下標(biāo)方法,也可以用指針?lè)椒ā?/p>

例7.13將字符串a(chǎn)復(fù)制到字符串b。

main()

{chara[]="Iamaboy.",b[20];

inti;

for(i=0;*(a+i)!='\0';i++)

*(b+i)=*(a+i); /*指針?lè)?/

*(b+i)='\0';

printf("stringais:%s\n",a);

printf("stringbis:");

for(i=0;b[i]!='\0';i++)

printf("%c",b[i]); /*下標(biāo)法*/

printf("\n");

}

程序運(yùn)行結(jié)果:

stringais:Iamaboy.

stringbis:Iamaboy.

程序中a和b都定義為字符數(shù)組,可以通過(guò)地址訪問(wèn)數(shù)組元素。在for語(yǔ)句中,先檢查a[i]是否為'\0'(此程序中a[i]是以*(a+i)形式表示的)。如果不等于'\0',表示字符串尚未處理完,就將a[i]的值賦給b[i],即復(fù)制一個(gè)字符。在for循環(huán)中將a串全部復(fù)制給了b串。最后還應(yīng)將'\0'復(fù)制過(guò)去,故有

*(b+i)='\0';

此時(shí)i的值是字符串有效字符的個(gè)數(shù)n加1。第二個(gè)for循環(huán)中用下標(biāo)法表示一個(gè)數(shù)組元素(即一個(gè)字符)。

也可以設(shè)指針變量,用它的值的改變來(lái)指向字符串中不同的字符。

例7.14

將兩個(gè)字符串s1和s2連接起來(lái),結(jié)果存放在s1中。要求使用字符指針加以實(shí)現(xiàn),不能使用strcat函數(shù)。

main()

{chars1[80],s2[80],*p1,*p2;

printf("Inputastring:");

gets(s1);

printf("Inputastring:");

gets(s2);

p1=s1;

p2=s2;

while(*p1!='\0')p1++;

while(*p2!='\0')*p1++=*p2++;*p1='\0';

printf("Theresultis:%s",s1);

}

程序運(yùn)行結(jié)果:

Inputastring:Bei

Inputastring:Jing

Theresultis:BeiJing

p1、p2是字符型指針變量,先使p1和p2分別指向字符串s1和s2的首地址,然后使p1指向字符串s1的末尾,即'\0'處,接下來(lái)將字符串s2連接到s1的末尾,最后還要在連接后的字符串末尾加上'\0',表示整個(gè)字符串的結(jié)束。

雖然用字符數(shù)組和字符指針變量都能實(shí)現(xiàn)字符串的存儲(chǔ)和運(yùn)算,但是它們二者之間是有區(qū)別的,不應(yīng)混為一談,主要有以下幾點(diǎn):

(1)字符指針變量的值可以改變,而字符數(shù)組名是一個(gè)常量,只能引用,不能改變。例如,以下語(yǔ)句是合法的:

char*p="CLanguage";

strcpy(p,"BASIC");

p++;

而若有如下定義:

charstr[]="CLanguage";

則以下語(yǔ)句是錯(cuò)誤的:

str="BASIC"; /*數(shù)組名是常量,不能被賦值*/

或strcpy(str,"BASIC");也是錯(cuò)誤的

str++; /*數(shù)組名是常量,不能自增、自減*/

(2)對(duì)字符指針變量賦初值:

char*a="IloveChina!";

它等價(jià)于

char*a;

strcpy(a,"ILoveChina!"); /*如果寫(xiě)成a="ILoveChina!"是 錯(cuò)誤的*/

而對(duì)數(shù)組的初始化:

charstr[14]={"IloveChina!"}

它不能等價(jià)于

charstr[14];

str[]="IloveChina!";

即數(shù)組可以在定義變量時(shí)整體賦初值,但不能在賦值語(yǔ)句中整體賦初值。

(3)如果定義了一個(gè)字符數(shù)組,在編譯時(shí),字符數(shù)組被分配連續(xù)的空白區(qū)域,不會(huì)與已有的數(shù)據(jù)區(qū)域發(fā)生沖突;而字符指針變量在未指向一個(gè)具體的字符數(shù)據(jù)時(shí),該指針變量中的值(地址值)是不確定的,有可能指向系統(tǒng)工作區(qū)域。直接用scanf函數(shù)賦值給尚未指向確定字符的指針變量,可能會(huì)破壞系統(tǒng)的正常工作,因?yàn)檩斎氲淖址赡軙?huì)覆蓋內(nèi)存中已有數(shù)據(jù)的區(qū)域。所以不宜用scanf直接給尚未指向確定字符的指針變量賦值。例如:

charstr[10];

scanf("%s",str);

是可以的。而下面的用法則是危險(xiǎn)的,不提倡:

char*a;

scanf("%s",a);其目的是輸入一個(gè)字符串,雖然一般也能運(yùn)行,但是不安全。因?yàn)榫幾g時(shí)雖然給指針變量a分配了內(nèi)存單元,a的地址(即&a)已指定,但a中存放的值(地址)并未指定,在a單元中是一個(gè)不可預(yù)料的值,即其指向是不確定的。在執(zhí)行scanf函數(shù)時(shí)要求將一個(gè)字符串輸入到a所指向的一段內(nèi)存單元(即以a的值(地址)開(kāi)始的一段內(nèi)存單元)中。而a的值如今卻是不可預(yù)料的,它可能指向內(nèi)存中空白的(未用的)用戶(hù)存儲(chǔ)區(qū)中(這是好的情況),也有可能指向已存放指令或數(shù)據(jù)的有用內(nèi)存段,這就會(huì)破壞程序,甚至破壞系統(tǒng),造成嚴(yán)重的后果。在程序規(guī)模較小時(shí),由于空白地帶較多,往往可以正常運(yùn)行,而程序規(guī)模較大時(shí),出現(xiàn)上述“沖突”的可能性就大多了。因此,應(yīng)當(dāng)如下處理:

char*a,str[10];

a=str;

scanf("%s",a);

即先使a有確定值,也就是使a指向一個(gè)數(shù)組的開(kāi)頭,然后輸入一個(gè)字符串,把它存放在以該地址開(kāi)始的若干單元中。

(4)用指針變量指向一個(gè)格式字符串,可以用它代替printf函數(shù)中的格式字符串。例如:

char*format;

format="a=%d,b=%f\n";

printf(format,a,b);

它相當(dāng)于

printf("a=%d,b=%f\n",a,b);

因此只要改變指針變量format所指向的字符串,就可以改變輸入和輸出的格式。這種printf函數(shù)稱(chēng)為可變格式輸出函數(shù)。

7.7.1指針數(shù)組

1.指針數(shù)組的概念

一個(gè)數(shù)組,其元素均為指針類(lèi)型數(shù)據(jù),稱(chēng)為指針數(shù)組。也就是說(shuō),指針數(shù)組中的每一個(gè)元素都相當(dāng)于一個(gè)指針變量,且指向相同數(shù)據(jù)類(lèi)型的數(shù)據(jù)。一維指針數(shù)組的定義形式如下:

類(lèi)型名*數(shù)組名[數(shù)組長(zhǎng)度]

例如:

int*p[4];7.7指針數(shù)組與二級(jí)指針由于[]比*優(yōu)先級(jí)高,因此p先與[4]結(jié)合,形成p[4]形式,這顯然是數(shù)組形式,它有4個(gè)元素。然后再與p前面的“*”結(jié)合,“*”表示此數(shù)組是指針類(lèi)型,每個(gè)數(shù)組元素(相當(dāng)于一個(gè)指針變量)都可指向一個(gè)整型變量。注意:不要寫(xiě)成int(*p)[4],這表示指向一維數(shù)組的行指針變量。

指針數(shù)組可以用來(lái)處理多維數(shù)組。例如定義:

intb[3][4],*pb[3];

由于數(shù)組b[3][4]可以看做由b[0]、b[1]、b[2]三個(gè)一維數(shù)組組成,因此可使指針數(shù)組的3個(gè)元素分別指向這3個(gè)數(shù)組,即

pb[0]=b[0];pb[1]=b[1];pb[2]=b[2];

這樣,通過(guò)指針pb就可以處理二維數(shù)組數(shù)據(jù)了。

例7.15

用指針數(shù)組處理二維數(shù)組數(shù)據(jù)。

#include<stdio.h>

main()

{intb[3][4],*pb[3],i,j;

for(i=0;i<3;i++)

for(j=0;j<4;j++)

b[i][j]=(i+1)*(j+1);

pb[0]=b[0];

/*一維數(shù)組名b[0]為列指針*/

pb[1]=b[1];

pb[2]=b[2];

for(i=0;i<3;i++)

{for(j=0;j<4;j++)

printf("b[%d][%d]=%d",i,j,*(pb[i]+j));

printf("\n");

}

}運(yùn)行情況如下:

b[0][0]=1b[0][1]=2b[0][2]=3b[0][3]=4

b[1][0]=2b[1][1]=4b[1][2]=6b[1][3]=8

b[2][0]=3b[2][1]=6b[2][2]=9b[2][3]=12

本例中,一維數(shù)組名b[0]、b[1]、b[2]為列指針,而指針數(shù)組元素pb[0]、pb[1]、pb[2]也只能接收列指針。注意,pb=b是錯(cuò)誤的形式,因?yàn)槎呔鶠閿?shù)組名,作為常量的數(shù)組名是不可以被賦值的。

2.字符指針數(shù)組和多個(gè)字符串的處理

一個(gè)字符指針變量可以指向一個(gè)字符串,那么字符指針數(shù)組就能指向多個(gè)字符串,其中每一個(gè)數(shù)組元素指向一個(gè)字符串。例如,5個(gè)同學(xué)的姓名,可用二維數(shù)組來(lái)存放:

charname[5][20]={"LiZhi","ZhangJi","JinXin",

"WangXiaofang","LiuYi"};

圖7.11表示了這種情況。圖7.11用二維數(shù)組存放多個(gè)字符串也可以用字符指針數(shù)組來(lái)指向多個(gè)字符串:

char*pname[5]={"LiZhi","ZhangJi","JinXin",

"WangXiaofang","LiuYi"};

圖7.12表示了這種情況。

用二維數(shù)組存放多個(gè)字符串,每行的長(zhǎng)度相同,可能會(huì)造成存儲(chǔ)空間的浪費(fèi)(如圖7.11所示),同時(shí)也限制了字符串的長(zhǎng)度。用指針數(shù)組時(shí),并未定義行的長(zhǎng)度,只是分別在內(nèi)存中存放了長(zhǎng)度不同的字符串,讓各個(gè)指針數(shù)組元素分別指向它們,沒(méi)有存儲(chǔ)單元的浪費(fèi)。因此用指針數(shù)組處理多個(gè)字符串效率較高,人們經(jīng)常用字符指針數(shù)組處理多個(gè)字符串。

圖7.12用指針數(shù)組指向多個(gè)字符串圖7.12中,pname數(shù)組各元素pname[0]、pname[1]、pname[2]、pname[3]、pname[4]中存放的內(nèi)容為各個(gè)字符串的首地址,也即字符“L”、“Z”、“J”、“W”、“L”在內(nèi)存中的地址。

下面舉例說(shuō)明指針數(shù)組對(duì)多個(gè)字符串的處理。

例7.16

在某班學(xué)生中查找指定姓名的學(xué)生。

首先定義一個(gè)字符指針數(shù)組,讓各數(shù)組元素指向各個(gè)姓名字符串;然后從鍵盤(pán)輸入一個(gè)欲查找的姓名,接下來(lái)用指針數(shù)組元素指向的各字符串與新輸入的字符串進(jìn)行比較,若相等則說(shuō)明找到,并退出循環(huán),否則繼續(xù)循環(huán),直到結(jié)束。循環(huán)結(jié)束后,根據(jù)結(jié)束循環(huán)時(shí)的情況,給出結(jié)論。

#include<stdio.h>

#include<string.h>

main()

{inti;

char*pname[5]={"LiZhi","ZhangJi","JinXin",

"WangXiaofang","LiuYi"};

charname[20];

printf("Enteraname:");

gets(name);

for(i=0;i<5;i++)

if(strcmp(pname[i],name)==0)

break;

if(i<5)

printf("%sisNo.%dstudent.\n",pname[i],i+1);

else

printf("%sisnotinthisclass.\n",name);

}程序運(yùn)行情況如下:

Enteraname:JinXin

JinXinisNo.3student.

Enteraname:JingXin

JingXinisNotinthisclass.

由于scanf函數(shù)輸入的字符串中不能含有空白字符,因此程序中使用gets函數(shù)來(lái)輸入姓名。

strcmp是字符串比較函數(shù),pname[i]是第i個(gè)字符串的起始地址,而name是欲查找字符串的起始地址。strcmp(pname[i],name)==0時(shí),說(shuō)明找到了所查字符串。7.7.2*指向指針的指針

多級(jí)指針是指指向指針數(shù)據(jù)的指針變量,簡(jiǎn)稱(chēng)為指向指針的指針,又稱(chēng)為二級(jí)指針,如圖7.13所示。其中,指針變量ppa為二級(jí)指針,指針變量pa為一級(jí)指針。*ppa將取出ppa指向的變量的值&a,等價(jià)于指針變量pa,即*ppa==pa==&a,則**ppa==*(*ppa)==*pa即為變量a。同樣,在圖7.12中,字符指針數(shù)組名pname就是一個(gè)二級(jí)指針,同理,pname+0、pname+1、pname+2等均為二級(jí)指針。因?yàn)槎?jí)指針?biāo)赶虻臄?shù)據(jù)為一級(jí)指針,所以*pname是一級(jí)指針pname[0](即pname[0]=*(pname

+0)=*pname),而*pname[0]或者**pname就是pname[0]指向的字符串的首字符'L'。

圖7.13二級(jí)指針ppa與變量a的指向聯(lián)系二級(jí)指針的說(shuō)明形式如下:

數(shù)據(jù)類(lèi)型**指針變量名;

其中,數(shù)據(jù)類(lèi)型是最終目標(biāo)變量的數(shù)據(jù)類(lèi)型。例如:

inta,*pa,**ppa;

char*pname[3],**ppname=pname;

上面定義中的ppa、pname、ppname均為二級(jí)指針,由此可見(jiàn),在定義指針變量時(shí),變量前有一個(gè)*的指針變量為一級(jí)指針,有兩個(gè)*的指針變量為二級(jí)指針。此外,指針數(shù)組名也為二級(jí)指針(如pname)。二級(jí)指針定義時(shí),變量名前面有兩個(gè)*號(hào),*運(yùn)算符的結(jié)合性是從右到左,因此**p相當(dāng)于*(*p),顯然*p是指針變量的定義形式。如果沒(méi)有最前面的*,那就是定義了一個(gè)指向字符數(shù)據(jù)的指針變量?,F(xiàn)在它前面又有一個(gè)*號(hào),表示指針變量p是指向另一個(gè)字符指針變量的。

針對(duì)圖7.13中的應(yīng)用,可參見(jiàn)如下程序段:

inta,*pa,**ppa;

a=5;

pa=&a;

ppa=&pa;

printf("%x%x%x\n",*ppa,pa,&a);

printf("%d%d%d\n",**ppa,*pa,a);該程序段是二級(jí)指針的簡(jiǎn)單應(yīng)用。因?yàn)?ppa==pa==&a,**ppa==*pa==a,所以,第一個(gè)printf輸出的結(jié)果均為相等的十六進(jìn)制地址值,第二個(gè)printf輸出的結(jié)果均為相等的數(shù)值5。

下面舉例說(shuō)明二級(jí)指針在字符串處理方面的應(yīng)用。

例7.17

輸出指針數(shù)組指向的各個(gè)字符串。

#include<stdio.h>

main()

{inti;

char*name[]={"Windows","MS-DOS","Unix","Linux"};

char**pp;

for(i=0;i<4;i++)

{pp=name+i;

/*使pp指向name[i],即pp=&name[i]*/

printf("%s\n",*pp);

}

}運(yùn)行情況如下:

Windows

MS-DOS

Unix

Linux

pp是二級(jí)指針變量,name+i也為二級(jí)指針。在第一次執(zhí)行循環(huán)體時(shí),賦值語(yǔ)句“pp=name+i;”使pp指向name數(shù)組的0號(hào)元素name[0],*pp

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論