![《C程序設(shè)計(jì)》課件第8章_第1頁](http://file4.renrendoc.com/view9/M01/17/01/wKhkGWdhHtSAX_FcAAOox93csL0184.jpg)
![《C程序設(shè)計(jì)》課件第8章_第2頁](http://file4.renrendoc.com/view9/M01/17/01/wKhkGWdhHtSAX_FcAAOox93csL01842.jpg)
![《C程序設(shè)計(jì)》課件第8章_第3頁](http://file4.renrendoc.com/view9/M01/17/01/wKhkGWdhHtSAX_FcAAOox93csL01843.jpg)
![《C程序設(shè)計(jì)》課件第8章_第4頁](http://file4.renrendoc.com/view9/M01/17/01/wKhkGWdhHtSAX_FcAAOox93csL01844.jpg)
![《C程序設(shè)計(jì)》課件第8章_第5頁](http://file4.renrendoc.com/view9/M01/17/01/wKhkGWdhHtSAX_FcAAOox93csL01845.jpg)
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
第八章指針8.1指針的概念與定義8.2指針作函數(shù)參數(shù)8.3指針與數(shù)組8.4指針與函數(shù)8.5復(fù)雜指針8.6程序設(shè)計(jì)舉例習(xí)題8.1指針的概念與定義8.1.1指針的概念指針就是用來存放地址的變量。某個(gè)指針存放了哪個(gè)變量的地址,就可以說該指針指向了這個(gè)變量。實(shí)際上,指針還可以指向數(shù)組、字符串等對象,甚至可以用來存放函數(shù)的入口地址。為了理解指針的概念,程序員要有關(guān)于計(jì)算機(jī)如何在存儲器中存儲信息的基本知識。以下簡單地介紹個(gè)人計(jì)算機(jī)中存儲器存儲的情況。個(gè)人計(jì)算機(jī)中CPU可以直接訪問的,用來存儲程序和數(shù)據(jù)的記憶部件稱為內(nèi)存儲器,內(nèi)存儲器由成千上萬個(gè)順序存儲單元組成,每個(gè)單元由一個(gè)惟一的地址標(biāo)識。給定計(jì)算機(jī)的存儲器地址范圍為從0到所安裝的存儲器數(shù)量的最大值。在計(jì)算機(jī)上運(yùn)行的每一個(gè)程序都要使用存儲器。例如,操作系統(tǒng)要占用一些計(jì)算機(jī)存儲空間,每個(gè)應(yīng)用程序也要占用計(jì)算機(jī)存儲空間。按照面向過程的結(jié)構(gòu)化程序設(shè)計(jì)方法,程序代碼和程序要處理的數(shù)據(jù)是分開存儲的,所以,一個(gè)程序在內(nèi)存中要占兩部分空間:數(shù)據(jù)部分和指令代碼部分。本節(jié)考察數(shù)據(jù)段在存儲器中的存儲情況。當(dāng)C程序中定義一個(gè)變量時(shí),編譯器劃分出一定數(shù)目的存儲器單元來存儲那個(gè)變量,存儲器單元的數(shù)目由變量的類型確定。編譯器將這幾個(gè)存儲單元與變量名聯(lián)系起來,當(dāng)程序引用這個(gè)變量名時(shí),自動(dòng)地訪問相應(yīng)的存儲器單元,當(dāng)然程序也可以通過該變量的地址來訪問這些存儲器單元。程序中的變量所需存儲單元的數(shù)目由變量的類型決定。例如,一個(gè)int變量占據(jù)的存儲單元可能為2字節(jié)(16位機(jī))或4字節(jié)(32位機(jī)),一個(gè)float變量為4字節(jié),一個(gè)char變量為1字節(jié)等。變量所占據(jù)的存儲單元的地址就是變量的地址,變量的地址表示為表達(dá)式
&變量名
&是單目運(yùn)算符,稱為取地址運(yùn)算符。例如:
charc=′A′;則&c是字符變量c的地址,即圖8.1中的FFF3H。圖8.1內(nèi)存分配表存放變量的地址的變量稱為指針。如果pc是存放字符變量地址的變量,則語句
pc=&c;將c的地址存入指針pc,如圖8.1所示,稱“pc指向c”,或“pc是c的指針”,被pc指向的變量c稱為“pc的對象”。“對象”就是一個(gè)有名字的內(nèi)存區(qū)域,即一個(gè)變量。
#include<stdio.h>
voidmain()
{
inti=0x3038;
charc=′A′;
int*pi=&i;
char*pc=&c;
printf("i=%x,c=%c,*pi=%d,*pc=%c\n",i,c,*pi,*pc);
printf("&i=%p,&c=%p,&pi=%p,&pc=%p\n",pi,pc,&pi,&pc);
}運(yùn)行結(jié)果:
i=3038,c=A,*pi=3038,*pc=A
&i=FFF4,&c=FFF3,&pi=FFF0,&pc=FFEE該程序在TurboC++3.0下編譯后,各變量的存儲內(nèi)容和內(nèi)存分配情況如圖8.1所示??梢钥吹?,在內(nèi)存中,整型變量i占2個(gè)字節(jié),字符型變量c占1個(gè)字節(jié),指針變量pi和pc各占2個(gè)字節(jié)(在這里,我們應(yīng)該看到,不管指針變量的類型是什么,它在內(nèi)存中所占的字節(jié)數(shù)是一定的;16位系統(tǒng)下是2個(gè)字節(jié),32位系統(tǒng)下是4個(gè)字節(jié)),這是因?yàn)橹羔槺4娴氖堑刂?,與具體類型是無關(guān)的。從圖8.1中可以看到,既可以通過變量名直接訪問內(nèi)存的數(shù)據(jù),又可以通過變量的地址(即指針)間接訪問該變量。指針類型是對所有類型的指針的總稱,指針的類型是指針?biāo)笇ο蟮臄?shù)據(jù)類型。例如,pc是指向字符變量的指針,簡稱字符指針。字符指針是基本類型的指針之一,除各種基本類型之外,允許說明指向數(shù)組的指針、指向函數(shù)的指針、指向結(jié)構(gòu)體和共用體的指針以及指向各類指針的指針。在C語言中只有指針被允許用來存放地址的值,其它類型的變量只能存放該類型的數(shù)據(jù)。(很多書中用指針一詞來指地址值,或用指針變量來代表指針,閱讀中應(yīng)注意其具體含義。)8.1.2指針的定義及使用
1.指針的定義指針是一種存放地址值的變量,像其它變量一樣,必須在使用前定義。指針變量的命名遵守與其它變量相同的規(guī)則,即必須是惟一的標(biāo)識符。指針定義的格式如下:類型名*指針名;其中,“類型名”說明指針?biāo)缸兞康念愋?;星號?”是一個(gè)指針運(yùn)算符,說明“指針名”是一個(gè)“類型名”類型的指針而不是一個(gè)“類型名”類型的變量。指針可與非指針變量一起說明,如例8.1所示。例8.1
指針與非指針的定義。
char*pcl,*pc2;/*pcl和pc2均為指向char型的指針*/
float*pf,percent;/*pf是float型的指針,而percent為普通的float型變量*/指針的類型是使用指針時(shí)必須注意的問題之一。在例8.1中,pc1、pc2和pf都是指針,它們存放的都是內(nèi)存的地址,但這并不意味著它們的類型相同。實(shí)際上pc1和pc2是同類型的,而它們與pf的類型卻不同。在使用中,pc1和pc2只能夠存放字符型變量的地址,而pf只能夠存放浮點(diǎn)型變量的地址。指針的當(dāng)前指向是使用指針的另一個(gè)需特別注意的問題。程序員定義一個(gè)指針之后,一般要使指針有明確指向,這一點(diǎn)可以通過對指針初始化或賦值來完成。與常規(guī)的變量未賦初值相同,沒有明確指向的指針不會(huì)引起編譯器出錯(cuò),但對于指針可能導(dǎo)致無法預(yù)料的或隱蔽的災(zāi)難性后果。例8.2
指針的指向。
int*point;
scanf("%d",point);例8.2中指向整型的指針point在定義之后直接使用了,這兩條語句在編譯時(shí)不會(huì)出現(xiàn)語法錯(cuò)誤,但在使用時(shí)卻幾乎肯定會(huì)出問題。表面上看,scanf()函數(shù)的參數(shù)要求給出的是地址,而point的值就代表的是地址,但是point的值究竟是多少,也就是說point究竟指向哪里,我們無法得知,在這種情況下就對point指向的單元進(jìn)行輸入操作,將沖掉point指向的單元的原有內(nèi)容,假如這個(gè)單元是操作系統(tǒng)的所在處,就破壞了操作系統(tǒng),顯然是一件危險(xiǎn)的事。
2.指針的有關(guān)運(yùn)算符兩個(gè)有關(guān)的運(yùn)算符:
&:取地址運(yùn)算符。*:指針運(yùn)算符(或稱“間接訪問”運(yùn)算符)。例如:&a為變量a的地址,*p為指針p所指向的存儲單元的內(nèi)容。
&運(yùn)算符只能作用于變量,包括基本類型變量和數(shù)組的元素、結(jié)構(gòu)體類型變量或結(jié)構(gòu)體的成員(具體內(nèi)容見第九章),不能作用于數(shù)組名、常量或寄存器變量。例如:
doubler,a[20];
inti;
registerintk;表達(dá)式&r、&a[0]、&a[i]是正確的,而&(2*r)、&a、&k是非法表示。單目運(yùn)算符*是&的逆運(yùn)算,它的操作數(shù)是對象的地址,*運(yùn)算的結(jié)果是對象本身。單目*稱為間訪運(yùn)算符,“間訪”就是通過變量的地址而不是變量名存取(或引用)變量。例如,如果pc是指向字符變量c的指針,則*(&c)和*pc表示同一字符對象c。因而賦值語句:*(&c)=′a′;*pc=′a′;
c=′a′;效果相同,都是將′a′存入變量c。指針可以由帶有地址運(yùn)算符的語句來使之有明確指向,如例8.3所示。例8.3
取地址運(yùn)算符。
intvariable,*point;
point=&variable;
3.指針的使用在定義了指針并明確了它的指向后,就可以使用指針了。例8.4和例8.5給出了指針使用的簡單例子。例8.4
指針的使用。
#include<stdio.h>
voidmain()
{
inta,b,*p1,*p2;
a=10;b=20;
p1=&a;p2=&b;
printf("%d\t%d\n",*p1,*p2);
p1=&b;p2=&a;
printf("%d\t%d\n",*p1,*p2);
}運(yùn)行結(jié)果:
10
20
20
10說明:
(1)在兩個(gè)printf()函數(shù)調(diào)用語句中的*是指針運(yùn)算符,這一單目運(yùn)算符的運(yùn)算對象應(yīng)該是指針或地址,它的作用是得到指針指向變量的值。
(2)在第一個(gè)printf()函數(shù)調(diào)用時(shí),可以假設(shè)內(nèi)存的分配如圖8.2所示。假設(shè)變量a占用的內(nèi)存單元為FFF4H和FFF5H,變量b占用的內(nèi)存單元為FFF2H和FFF3H,指針p1占用的內(nèi)存單元為FFF0H和FFF1H,指針p2占用的內(nèi)存單元為FFEFH和FFF0H,則它們的值分別是:10(000AH),20(0014H),F(xiàn)FF2H,F(xiàn)FF4H。*p1和*p2的值分別是20(0014H)和10(000AH),這就是第一個(gè)printf()函數(shù)調(diào)用所得到的結(jié)果。圖8.2內(nèi)存分配表(一)
(3)在第二個(gè)printf()函數(shù)調(diào)用時(shí),可以假設(shè)內(nèi)存的分配如圖8.3所示。圖8.3內(nèi)存分配表(二)此時(shí)各變量所占用的內(nèi)存單元不變,但p1和p2的值分別為FFF4H和FFF2H,也就是它們此時(shí)分別指向b和a。這樣*p1和*p2的值分別是10(000AH)和20(0014H),所以第二個(gè)printf()函數(shù)調(diào)用所得到的結(jié)果為10(000AH)和20(0014H)。
例8.5
指針的使用。
#include<stdio.h>
voidmain()
{
inta,*pi;
floatf,*pf;
a=10;f=20.5;
pi=&a;pf=&f;
printf("%d\t%4.1f\n",a,f);
printf("%d\t%4.1f\n",*pi,*pf);
}運(yùn)行結(jié)果:
1020.5
1020.5
說明:
(1)printf()函數(shù)調(diào)用時(shí),可以假設(shè)內(nèi)存的分配如圖8.4所示。圖8.4內(nèi)存分配表假設(shè)變量a占用的內(nèi)存單元為FFF4H和FFF5H,浮點(diǎn)型變量f占用的內(nèi)存單元為FFEEH,F(xiàn)FEFH,F(xiàn)FF0H和FFF1H,指針pi占用的內(nèi)存單元為FFF2H和FFF3H,指針pf占用的內(nèi)存單元為FFECH和FFEDH,則它們的值分別是:10(0AH),20.5(3F80FF00H),F(xiàn)FF4H,F(xiàn)FEEH。*pi和*pf的值分別是10(0AH)和20.5(3F80FF00H),這就是printf()函數(shù)調(diào)用所得到的結(jié)果。
(2)printf()函數(shù)調(diào)用語句中的*pi和*pf雖然都是指針運(yùn)算,但*pi是將pi的值(2000)開始的兩個(gè)內(nèi)存單元的值(10)作為運(yùn)算結(jié)果,而*pf是將pf的值(2002)開始的四個(gè)內(nèi)存單元的值作為運(yùn)算結(jié)果。這就是指針類型不同所帶來的結(jié)果,指針運(yùn)算符會(huì)根據(jù)它的運(yùn)算對象的類型進(jìn)行相應(yīng)的運(yùn)算。
(3)*既可用作指針運(yùn)算符,也可用作乘號運(yùn)算符。不必?fù)?dān)心編譯器不能分辨。在*附近總會(huì)有足夠的信息使編譯器能分辨是指針運(yùn)算符還是乘號。
(4)用變量名來訪問變量的內(nèi)容稱為直接訪問;用指針來訪問變量的內(nèi)容稱為間接訪問,即間接地從指針中找到地址值,再據(jù)此地址訪問變量。直接訪問和間接訪問的結(jié)果是一樣的,正如本例中的兩條printf輸出的結(jié)果是相同的。由于&和*運(yùn)算符在使用指針時(shí)經(jīng)常使用,二者優(yōu)先級相同,結(jié)合方向?yàn)樽杂抑磷?,下面進(jìn)行一些說明。假設(shè)已定義了整型變量a和整型指針point,并已執(zhí)行了“point=&a;”語句,則:
(1)&*point的含義,根據(jù)&和*運(yùn)算符的優(yōu)先級和結(jié)合性可知,先進(jìn)行*point運(yùn)算,它就是變量a,再執(zhí)行&運(yùn)算。因此&*point和&a相同,也就是point本身。
(2)*&point的含義,根據(jù)&和*運(yùn)算符的優(yōu)先級和結(jié)合性可知,先進(jìn)行&point運(yùn)算,得到point的地址值,因?yàn)閜oint作為一個(gè)變量,在內(nèi)存中分配單元,所以有確定的地址值。再執(zhí)行*運(yùn)算,又得到point的值。
(3)*&a的含義,根據(jù)&和*運(yùn)算符的優(yōu)先級和結(jié)合性可知,先進(jìn)行&a運(yùn)算,得到a的地址值,它就是指針point的值,再執(zhí)行*運(yùn)算,因此*&a和*point相同,也就是a本身。
(4)&*a的含義,根據(jù)&和*運(yùn)算符的優(yōu)先級和結(jié)合性可知,先進(jìn)行*a運(yùn)算,此時(shí)將整型變量a作為一個(gè)指針來運(yùn)算,將a的值作為一個(gè)地址值對待,這是不允許的,所以&*a不允許使用。
(5)(*point)++相當(dāng)于a++。如果去掉括號,即成為*point++,根據(jù)運(yùn)算符的優(yōu)先級和結(jié)合性,它相當(dāng)于*(point++),這時(shí)先按point的原值進(jìn)行*運(yùn)算,得到a的值。然后使point的值改變,之后point就不再指向a了。8.2指針作函數(shù)參數(shù)前面已經(jīng)講過,C在函數(shù)調(diào)用時(shí)參數(shù)的傳遞是按照單向值傳遞的方式,因而在被調(diào)用函數(shù)中形參的變化不能改變實(shí)參變量的值。
例8.6
函數(shù)參數(shù)的傳遞。
#include<stdio.h>
voidswap(intx,inty);
main()
{
inta,b;
a=10;b=20;
swap(a,b);
printf("a=%d,b=%d\n",a,b);
}
voidswap(intx,inty)
{
inttemp;
temp=x;
x=y;
y=temp;
}運(yùn)行結(jié)果:
a=10,b=20可以看到,雖然在swap()函數(shù)中交換了x和y的值,但main()函數(shù)中a和b的值并未交換。其原因可在圖8.5和圖8.6中看出,在swap()函數(shù)調(diào)用時(shí),a和b的值傳遞給x和y,而在swap()函數(shù)調(diào)用結(jié)束時(shí)雖然x和y的值交換了,但是它們所占用的存儲單元也隨swap()函數(shù)調(diào)用結(jié)束而釋放了,a和b的值并未改變。圖8.5swap()函數(shù)被調(diào)用時(shí)的內(nèi)存分配圖圖8.6swap()函數(shù)調(diào)用結(jié)束時(shí)的內(nèi)存分配圖為了達(dá)到交換主調(diào)函數(shù)中的變量a和b的目的,需要用指針作參數(shù)。通過將主調(diào)函數(shù)中變量的地址傳給被調(diào)用函數(shù)的指針形參,這樣,在被調(diào)用函數(shù)中對形參所指向內(nèi)容進(jìn)行交換就是對主調(diào)函數(shù)中的變量a、b交換,從而達(dá)到交換變量a、b的值這一目的。
例8.7
指針作函數(shù)參數(shù)。
#include<stdio.h>
voidswap(int*x,int*y);
main()
{
inta,b,*p1,*p2;
a=10;b=20;p1=&a;p2=&b;
swap(p1,p2);
printf(“a=%d,b=%d\n”,a,b);
/*或printf("%d%d",*p1,*p2)*/
}
voidswap(int*pa,int*pb)
{
inttemp;
temp=*pa;*pa=*pb;*pb=temp;
}運(yùn)行結(jié)果:
a=20,b=10可以看到,上例中主函數(shù)中以整型指針p1、p2作為swap()函數(shù)的實(shí)參,調(diào)用前已用賦值語句使p1指向變量a,p2指向變量b。swap()函數(shù)的形參pa和pb也是整型指針。從圖8.7中可以看到,在swap()函數(shù)被調(diào)用時(shí),實(shí)參指針p1和p2分別把它們的值傳遞給形參指針pa和pb,可以認(rèn)為指針p1和pa都指向變量a,而指針p2和pb都指向變量b。所以*pa就是引用a,*pb就是引用b,交換*pa和*pb就是交換a和b。*pa和*pb既是swap()函數(shù)的輸入?yún)?shù)又是輸出參數(shù),進(jìn)入swap()時(shí),*pa和*pb是交換之前的a、b,從swap()返回時(shí),*pa和*pb是交換后的a、b,因?yàn)樾螀?、?shí)參本來就指向同一內(nèi)存單元,相當(dāng)于形參所指向內(nèi)容的改變“返回”給了實(shí)參所指的內(nèi)容(如圖8.8所示)??梢?,通過將變量的地址傳遞給被調(diào)函數(shù),可在被調(diào)函數(shù)中改變這些變量的值。圖8.7swap()函數(shù)被調(diào)用時(shí)的內(nèi)存分配圖圖8.8swap()函數(shù)調(diào)用結(jié)束時(shí)的內(nèi)存分配圖程序運(yùn)行的結(jié)果使a和b的值得以交換。是不是用指針作函數(shù)參數(shù)時(shí),參數(shù)的傳遞就不是單向值傳遞了呢?事實(shí)上,用指針作函數(shù)參數(shù)時(shí),參數(shù)的傳遞仍然符合由實(shí)參到形參的單向值傳遞。從圖8.7可以看到,在函數(shù)swap()調(diào)用過程中,交換了pa和pb所指向變量的內(nèi)容(注意,并未交換pa和pb本身的值),也就是交換了變量a和b的值。形參pa,pb和實(shí)參p1,p2本身的值均未改變。
swap()的形參pa和pb都是整型指針。于是,調(diào)用swap()時(shí),傳給pa和pb的分別是a的地址和b的地址,為了對指針作函數(shù)參數(shù)進(jìn)行充分說明,將例8.7進(jìn)行一些改變,讓我們來分析下面兩個(gè)例子。例8.8
指針作函數(shù)參數(shù)。
#include<stdio.h>
voidswap(int*,int*);
voidmain()
{
inta,b,*p1,*p2;
a=10;b=20;p1=&a;p2=&b;
swap(p1,p2);
printf("a=%d,b=%d\n",a,b);
}
voidswap(int*pa,int*pb)
{
int*temp;
temp=pa;
pa=pb;
pb=temp;
}運(yùn)行結(jié)果:
a=10,b=20例8.8與例8.7的不同之處在于例8.8的swap()函數(shù)中交換的是pa和pb本身的值,而并未交換它們所指向的變量a和b的值,所以最后得到的結(jié)果是變量a和b的值并未交換。從圖8.9和圖8.10中可以看到,函數(shù)swap()調(diào)用結(jié)束時(shí)指針pa和pb的值進(jìn)行了交換,但這一交換不會(huì)影響到變量a和b,同樣也不會(huì)影響到指針p1和p2,這完全符合函數(shù)參數(shù)的單向值傳遞。圖8.9swap()函數(shù)被調(diào)用時(shí)的內(nèi)存分配圖圖8.10swap()函數(shù)調(diào)用結(jié)束時(shí)的內(nèi)存分配圖
例8.9
指針作函數(shù)參數(shù)。
#include<stdio.h>
voidswap(int*,int*);
voidmain()
{
inta,b,*p1,*p2;
a=10;b=20;p1=&a;p2=&b;
swap(p1,p2);
printf("a=%d,b=%d\n",a,b);
}
voidswap(int*pa,int*pb)
{
int*temp;*temp=*pa;*pa=*pb;*pb=*temp;
}對于這個(gè)例子我們沒有給出運(yùn)行結(jié)果,這是因?yàn)檫@個(gè)例子中有不安全的因素。在swap()函數(shù)中的指針temp在沒有明確指向的情況下就試圖對它所指向的變量進(jìn)行了操作,這是指針使用中所不允許的。當(dāng)然,也許在某些系統(tǒng)中,這個(gè)程序可以得到運(yùn)行結(jié)果,即
a=20,b=10但這個(gè)程序還是有問題的,必須要經(jīng)過改進(jìn)才可以??梢詫wap()函數(shù)改變成這樣:
voidswap(int*pa,int*pb)
{
int*temp,t;
temp=&t;*temp=*pa;*pa=*pb;*pb=*temp;
}
此時(shí)可以得到運(yùn)行結(jié)果:
a=20,b=10其原因與例8.7一樣,這里不再重復(fù)。通過以上例題可以總結(jié)出:如果想通過函數(shù)調(diào)用得到n個(gè)要改變的值,可以:
(1)在主調(diào)函數(shù)中設(shè)n個(gè)變量,用n個(gè)指針指向它們。
(2)用指針作實(shí)參,將這n個(gè)變量的地址傳給所調(diào)用的函數(shù)的形參(也可直接用n個(gè)變量的地址作實(shí)參來簡化以上兩步)。
(3)通過形參指針,改變該n個(gè)變量的值。
(4)在主調(diào)函數(shù)中使用這些改變了值的變量。其它具體示例可參考《〈C程序設(shè)計(jì)〉學(xué)習(xí)指導(dǎo)(第二版)》中本章的典型例題。不論是用普通變量還是用指針作函數(shù)參數(shù),都不違反函數(shù)參數(shù)的由實(shí)參到形參的單向值傳遞,只是用指針作函數(shù)參數(shù)時(shí),因?yàn)橹羔樀闹稻褪堑刂?,所以傳遞的是地址,此時(shí)雖然形參的改變?nèi)詿o法返回給實(shí)參,但利用形參指針對其所指向單元內(nèi)容的操作,就有可能改變主調(diào)函數(shù)中的變量的值。這樣往往使函數(shù)的功能更為完善和強(qiáng)大,C的許多標(biāo)準(zhǔn)函數(shù)都采用這種方式,結(jié)構(gòu)化程序設(shè)計(jì)方法要求函數(shù)間以此種方式來傳遞數(shù)據(jù),所以這是指針的一個(gè)很重要的應(yīng)用,需要進(jìn)行大量的實(shí)踐去體會(huì)與掌握。8.3指針與數(shù)組
C語言中數(shù)組與指針有著密切的聯(lián)系,在編程時(shí)完全可以用指針代替下標(biāo)引用數(shù)組的元素,且使數(shù)組的引用更為靈活、有效。當(dāng)一個(gè)數(shù)組被定義后,程序會(huì)按照其類型和長度在內(nèi)存中為數(shù)組分配一塊連續(xù)的存儲單元。數(shù)組名成為符號常量,其值為數(shù)組在內(nèi)存中所占用單元的首地址,也就是說數(shù)組名就代表了數(shù)組的首地址。指針就是用來存放地址的變量,當(dāng)某個(gè)指針存放數(shù)組中第一個(gè)元素的地址時(shí),可以說該指針指向了這個(gè)數(shù)組,這樣我們可以通過指針運(yùn)算間接訪問數(shù)組中的元素。8.3.1指向一維數(shù)組的指針我們已經(jīng)知道,如下的語句:
inta[10];定義了a是長度為10的一個(gè)整型數(shù)組,a[i](i=0,1,…,9)是a的第i個(gè)元素。為了用指針表示a的元素,需要定義一個(gè)與a的元素同類型的指針,例如:
int*pa;并由賦值語句
pa=&a[0];或pa=a;使pa指向a的第0個(gè)元素,習(xí)慣上稱為使pa指向數(shù)組a,如圖8.11所示。也可在定義時(shí)賦初值,即
inta[10],*pa=a;或
inta[10],*pa=&a[0];然后,只要移動(dòng)指針pa,就可訪問數(shù)組a的任一元素。圖8.11指向數(shù)組元素的指針如果pa指向a[0],則(pa+i)指向a[i];如果pa指向a[i],則pa+1(pa不變)或++pa(pa增1)指向a[i+1]。如果pa指向a[0],則*pa等價(jià)于a[0];如果(pa+i)指向a[i],則*(pa+i)等價(jià)于a[i]。同理,如果(pa+1)指向a[i+1],則*(pa+1)等價(jià)于a[i+1]。概括地說,指向數(shù)組的指針加1等效于數(shù)組元素的下標(biāo)加1。類似的,如果pa指向a[i],則*(++pa)等價(jià)于a[++i]。實(shí)際上,C語言允許這樣的表達(dá)方式:pa[i]和*(a+i),它們等價(jià)于*(pa+i)和a[i]。由此可見,引用數(shù)組元素有兩種等價(jià)的形式:通過下標(biāo)引用和通過指針引用。以數(shù)組a為例,假定pa指向a[0],元素的下標(biāo)引用和指針引用的對應(yīng)關(guān)系如下(寫在同一行上的表達(dá)式是對同一元素的等價(jià)引用形式):
a[0] *pa *a或*(a+0) a[1] *(pa+1) *(a+1) a[2] *(pa+2) *(a+2) a[9] *(pa+9) *(a+9)元素地址的對應(yīng)關(guān)系如下:
&a[0] pa a或a+0 &a[1] pa+1 a+1 &a[2] pa+2 a+2 &a[9] pa+9 a+9
注意:指針pa是變量,數(shù)組名a是常量,因而
pa=a;pa++;是合法的操作,而
a=pa;a++;pa=&a;都是非法的。說明:
(1)如果數(shù)組名a的值為2000,也就是數(shù)組a在內(nèi)存中的首地址為2000。若執(zhí)行了pa=a,則pa的值也為2000。那么,pa+2的值是多少呢?pa+2的值為2000+2×sizeof(int),假設(shè)在我們所使用的系統(tǒng)中int型所占的字節(jié)為2,則pa+2的值為2004,即元素a[2]的地址。指針加1,不是簡單地將指針的值加1,而是指向下一個(gè)數(shù),系統(tǒng)會(huì)根據(jù)類型自動(dòng)地計(jì)算地址。
(2)當(dāng)指針指向數(shù)組時(shí),可通過數(shù)組名和指針兩種方式來訪問數(shù)組元素,因?yàn)橹羔樖亲兞慷鴶?shù)組名是常量,所以指針的值可以改變,這就需要特別注意指針的當(dāng)前指向,是指向了數(shù)組的哪個(gè)元素?還是已經(jīng)指向了數(shù)組所占內(nèi)存空間以外的地方?如果已經(jīng)指向了數(shù)組所占內(nèi)存空間以外的地方,則一般會(huì)出問題,這是指針使用中常出錯(cuò)之處,也是指針使用中最危險(xiǎn)之處。
(3)*pa++的意義:*和++的優(yōu)先級相同,且為右結(jié)合性,*pa++則相當(dāng)于*(pa++),它與(*pa)++是不同的,雖然兩個(gè)表達(dá)式的值是相同的。前者的意思是先取*pa的值,并作為表達(dá)式的值,后使pa加1;后者是先取*pa的值,并作為表達(dá)式的值,后使*pa加1。注意,兩者自增的對象是不同的。8.3.2數(shù)組作函數(shù)參數(shù)當(dāng)我們需要將數(shù)組的首地址作為函數(shù)參數(shù)來傳遞時(shí),可采用的方式有兩種。一種是用指向數(shù)組的指針作為函數(shù)參數(shù);另一種是直接用數(shù)組名作為函數(shù)參數(shù)。指針和數(shù)組名都可以作為形參和實(shí)參,它們既可以同時(shí)作為形參和實(shí)參,也可以分別作為形參和實(shí)參。這樣可有四種情況:
(1)形參和實(shí)參都是指針時(shí),這種情況與8.2節(jié)所討論的問題相似,只是此時(shí)實(shí)參存放的是某個(gè)數(shù)組的首地址,并把這個(gè)地址傳遞給形參。
(2)實(shí)參是數(shù)組名,形參是指針時(shí),這種情況是同類型的常量實(shí)參傳遞給變量形參,此時(shí),對形參指針?biāo)赶騼?nèi)容的訪問就是對數(shù)組的訪問。
(3)形參和實(shí)參都是數(shù)組名時(shí),這時(shí)有一些特殊性。因?yàn)榘凑諗?shù)組的定義,數(shù)組名應(yīng)該是存放數(shù)組首地址的常量,而此時(shí)形參的值卻會(huì)在函數(shù)調(diào)用時(shí)得到實(shí)參傳遞的值,這豈不是矛盾了嗎?實(shí)際上,雖然有實(shí)參數(shù)組名和形參數(shù)組名兩個(gè)數(shù)組名,卻只有一個(gè)數(shù)組,也就是在這種情況下,C語言不會(huì)給形參數(shù)組再開辟一個(gè)數(shù)組的內(nèi)存單,而是認(rèn)為形參數(shù)組名是實(shí)參數(shù)組的別名,也就是說,對形參數(shù)組的操作就是對實(shí)參數(shù)組的操作。
(4)實(shí)參是指針,形參是數(shù)組名,這與上一問題有些相似。系統(tǒng)認(rèn)為實(shí)參指針是指向某個(gè)數(shù)組的,此時(shí)形參數(shù)組與實(shí)參所指向的數(shù)組是同一數(shù)組,且為該數(shù)組的別名。
例8.10
數(shù)組名作函數(shù)參數(shù)。程序功能:用選擇法對10個(gè)整數(shù)排序。
#include<stdio.h>
voidmain()
{
int*p,i,a[10];
p=a;
for(i=0;i<10;i++)
scanf("%d",p++);
p=a;
sort(p,10);
for(p=a,i=0;i<10;i++)
{
printf("%5d",*p);
p++;}printf("\n");
}
voidsort(intx[],intn)
{
inti,j,k,t;
for(i=0;i<n-1;i++)
{k=i;
for(j=i+1;j<n;j++)
if(x[j]>x[k])k=j;
if(k!=i)
{t=x[i];x[i]=x[k];x[k]=t;}
}
}函數(shù)sort()的形參x可以認(rèn)為是main()函數(shù)中數(shù)組a的別名,所以在函數(shù)sort()中對x的操作就是對a的操作,使得數(shù)組a得以排序,完成了程序的要求。注意在main()函數(shù)中使用指針p時(shí),指針當(dāng)前指向的變化。當(dāng)然,main()函數(shù)中調(diào)用sort()函數(shù)時(shí)實(shí)參可以是指針,也可以是數(shù)組名,在函數(shù)sort()中形參可以是數(shù)組名,也可以是指針??蓪⑸侠某绦蚋膶懭缦拢?/p>
#include<stdio.h>
voidsort(int*,int);
voidmain()
{
inti,a[10];
for(i=0;i<10;i++)
scanf("%d",&a[i]);
sort(a,10);
for(i=0;i<10;i++)
printf("%5d",a[i]);
}
voidsort(int*x,intn)
{
inti,j,k,t;
for(i=0;i<n-1;i++)
{
k=i;
for(j=i+1;j<n;j++)
if(x[j]>x[k])k=j;
if(k!=i)
{t=x[i];x[i]=x[k];x[k]=t;
}
}可以看到在函數(shù)sort()中,除了對形參x的類型說明有一點(diǎn)變化,程序的其它部分沒有改變。雖然x此時(shí)是指針,用它訪問數(shù)組元素時(shí)可以用下標(biāo)法,也可用指針法。如將x[j]寫成*(x+j),x[k]寫成*(x+k),x[i]寫成*(x+i)。另外,此例值得注意之處是對“自頂向下,逐步細(xì)化,模塊化”的結(jié)構(gòu)化程序設(shè)計(jì)思想的典型體現(xiàn)。在第七章的開始,我們已講述了結(jié)構(gòu)化的程序設(shè)計(jì)思想,強(qiáng)調(diào)一個(gè)函數(shù)應(yīng)只完成單一的任務(wù)。對于本題用選擇法排序便是這樣一個(gè)功能單一的最小任務(wù)。而其它功能,如數(shù)組的輸入/輸出等則在其主調(diào)函數(shù)中完成,這是高級語言編寫結(jié)構(gòu)化程序的常見模式。8.3.3指針和字符串在C語言中,字符串(例如"Iamastudent")指在內(nèi)存中存放的一串以′\0′結(jié)尾的若干個(gè)字符。在沒有學(xué)習(xí)指針之前,我們已經(jīng)知道可以用字符數(shù)組來表達(dá)一個(gè)字符串。例如,可以這樣定義和初始化一個(gè)字符數(shù)組:
charstring[]="Iamastudent";數(shù)組長度由字符串長度加1確定。也可以定義一個(gè)字符數(shù)組,然后用標(biāo)準(zhǔn)輸入函數(shù)從外部設(shè)備讀入一個(gè)字符串。例如:
charstring[20];
scanf("%s",string);數(shù)組長度應(yīng)能足夠存放讀入的最大長度的字符串。利用指針也可以表達(dá)字符串,而且比用字符數(shù)組更為方便靈活。例如,可以這樣定義和初始化一個(gè)字符指針:
char*point="Iamastudent";point是指向字符串"Iamastudent"的指針,即字符串的首地址賦給了字符指針,因此使一個(gè)字符指針指向一個(gè)字符串。也可以采用下面的方式:
char*point;
point="Iamastudent";賦值語句“point="Iamastudent";”不是串拷貝,實(shí)際上,字符串常量"Iamastudent"的值就是該字符串在內(nèi)存中的首地址,這樣這個(gè)賦值語句就很容易理解了,相當(dāng)于指針point中存放了字符串的首地址,從這一點(diǎn)也可以看出字符串與指針的關(guān)系更為密切。下面這種情況同樣是不允許的:
char*point;
scanf("%s",point);其原因是指針沒有明確的指向,其值是任意的,也許所指向的區(qū)域不是用戶可以訪問的內(nèi)存區(qū)域,或是根本不存在的地方。雖然字符數(shù)組和字符指針都可以用來表達(dá)字符串,但它們還是有不同之處。例如:
charstring[]="Iamastudent";
char*point="Iamastudent";string和point的值都是字符串"Iamastudent"的首地址,但string是一個(gè)字符數(shù)組,名字本身是一個(gè)地址常量,而point是值為字符數(shù)組首地址(第0個(gè)元素的地址)的指針。因而point可以被賦值,而string不能,即
point="Iamastudent";合法
string=“Iamastudent”;非法指向字符串的指針常常出現(xiàn)在函數(shù)參數(shù)中,下面舉幾個(gè)例子。
例8.11
字符串拷貝函數(shù)。
voidmy_strcpy(char*t,char*s)
{
while((*t=*s)!=′\0′)
{
s++;
t++;
}
}函數(shù)my_strcpy()將串s復(fù)制到串t,準(zhǔn)確地講是將s指向的字符串復(fù)制到由t指向的字符數(shù)組。開始時(shí),t和s分別指向兩個(gè)實(shí)參數(shù)組的頭元素,復(fù)制一個(gè)元素;然后各自的指針移向下一個(gè)元素,復(fù)制下一個(gè)元素;……這一過程進(jìn)行到字符′\0′被復(fù)制為止,此時(shí)串s被全部復(fù)制到t。調(diào)用my_strcpy()時(shí),對應(yīng)于目的串t的實(shí)參可以是字符數(shù)組名或指向字符數(shù)組的指針;對應(yīng)于源串s的實(shí)參可以是字符串、字符數(shù)組名或指向字符串的指針。例如:
chars1[20],s2[20],*ps1,*ps2;下面對my_strcpy()函數(shù)的調(diào)用都是正確的:
(1)my_strcpy(s1,"Iamastudent");
(2)ps1=&s1[0];
ps2="Iamastudent";
my_strcpy(ps1,ps2);
(3)ps2=&s2[0];
my_strcpy(ps2,"Iamastudent");
my_strcpy(s1,s2);或my_strcpy(s1,ps2);以上三組語句是等效的,都是將串"Iamastudent"復(fù)制到字符數(shù)組s1。
my_strcpy的定義可以寫成更簡練的形式:
voidmy_strcpy(char*t,char*s)
{
while((*t++=*s++)!=′\0′);
}復(fù)制過程繼續(xù)的條件是被復(fù)制的字符為非0(非′\0′)。由于組成字符串的任何字符(′\0′除外)的值都為非0,因此my_strcpy()還可以進(jìn)一步簡化為:
voidmy_strcpy(char*t,char*s)
{
while(*t++=*s++);
}例8.12
字符串比較函數(shù)。
intmy_strcmp(char*s,char*t){
for(;*s==*t;s++,t++)
if(*s==′\0′)return0;
return(*s-*t);
}函數(shù)my_strcmp()按字典順序逐個(gè)比較串s和t的每個(gè)對應(yīng)元素,如果s和t的長度相同且所有元素都相等,則s和t相等,my_strcmp()返回值0;否則s和t不相等。當(dāng)?shù)谝淮纬霈F(xiàn)不相等元素時(shí),my_strcmp()結(jié)束比較,較小的那個(gè)字符所在的那個(gè)串較小,反之為大。當(dāng)s<t時(shí),返回值小于0;當(dāng)s>t時(shí),返回值大于0。8.3.4指向多維數(shù)組的指針在研究多維數(shù)組的問題時(shí),我們可以將數(shù)組僅僅看作是C語言的一個(gè)構(gòu)造類型,其元素可以是C語言的任何類型,包括數(shù)組本身。也就是說,數(shù)組可以作為另一個(gè)數(shù)組的數(shù)組元素。這樣,就不存在多維數(shù)組的問題了??梢哉f在C語言中,數(shù)組在實(shí)現(xiàn)方法上只有一維的概念,多維數(shù)組被看成以下一級數(shù)組為元素的數(shù)組。設(shè)有一個(gè)二維數(shù)組的定義為:
staticinta[2][4]={{1,3,5,7},{2,4,6,8}};表面上看,a是一個(gè)二維數(shù)組名,我們也可以將它看成是一個(gè)一維數(shù)組名。它包含兩個(gè)數(shù)組元素,分別為a[0]和a[1]。每個(gè)數(shù)組元素又包含四個(gè)元素,例如,數(shù)組a[0]包含四個(gè)元素,分別為:a[0][0]、a[0][1]、a[0][2]和a[0][3]。a[0]和a[1]雖然沒有單獨(dú)地、顯式地定義,它們卻可以被認(rèn)為是數(shù)組名,是數(shù)組在內(nèi)存中的首地址,這一點(diǎn)與數(shù)組名a一樣,與a不同的是類型,也就是數(shù)組元素的類型不同。a[0]和a[1]數(shù)組的元素類型為整型數(shù),而a數(shù)組的元素類型為整型數(shù)組。我們可以假設(shè)數(shù)組a在內(nèi)存中的分配情況如下:可以看到對于數(shù)組a來說,它所占用的內(nèi)存空間是連續(xù)的。如果我們將a視為一維數(shù)組的話,那么它的兩個(gè)數(shù)組元素a[0]和a[1]所占用的內(nèi)存也是連續(xù)的,此時(shí)每個(gè)數(shù)組元素占用8個(gè)內(nèi)存單元。當(dāng)然如果將a視為二維數(shù)組的話,它的8個(gè)數(shù)組元素所占用的內(nèi)存也是連續(xù)的,此時(shí)每個(gè)數(shù)組元素占用2個(gè)內(nèi)存單元。數(shù)組a一旦有了以上的定義后,在C語言的程序中可用的與數(shù)組a有關(guān)的表示形式有很多。為了更清楚地說明,我們可以把二維數(shù)組a看成是一個(gè)兩行四列的形式。這樣對于二維數(shù)組可以認(rèn)為a為首行地址(即第0行地址),而a+1為第1行地址;a[0]為首行首列地址,而a[0]+1為第0行第1列地址。詳細(xì)區(qū)分說明如下:
對行地址進(jìn)行一次指針運(yùn)算就成為列地址,而對列地址進(jìn)行一次取地址運(yùn)算就成為行地址,這就很容易理解雖然a+1和*(a+1)具有相同的值,但卻表示不同的類型。
例8.13
多維數(shù)組。
#include<stdio.h>
voidmain()
{
staticinta[3][4]={{1,3,5,7},{2,4,6,8},{10,20,30,40}};
int*p;
for(p=a[0];p<a[0]+12;p++)/*注1*/
{
if((p-a[0])%4==0)printf("\n");/*注2*/
printf("%4d",*p);
}
}運(yùn)行結(jié)果:
1357
2468
10203040程序中指針p為一個(gè)可以存放整型量地址的變量,在程序的第5行將第0行第0列地址賦給它,實(shí)際上行地址也是某個(gè)整型量的地址,所以可以這樣做。如果將注1行的a[0]改成a,是否可以呢?不行!雖然a和a[0]的值相同,但類型卻不同,這樣做是不合法的。那么是否可以將注2行的a[0]用a替換呢?也不行!因?yàn)橹羔樀募訙p運(yùn)算結(jié)果是受到類型影響的,不同類型之間的運(yùn)算是無法進(jìn)行的。既然我們用整型指針來存放列地址,那么如何定義行指針來存放行地址呢?下面就是行指針的定義方式。類型名(*指針名)[數(shù)組長度];約束行指針類型的條件有兩個(gè),一是它所指向數(shù)組的類型;一是每行的列數(shù)。下面用行指針來改寫例8.13。
例8.14
多維數(shù)組。
#include<stdio.h>
voidmain()
{
staticinta[3][4]={{1,3,5,7},{2,4,6,8},{10,20,30,40}};
inti,j,(*p)[4];
p=a;
for(i=0;i<3;i++)
{
for(j=0;j<4;j++)
printf("%4d",*(*(p+i)+j));
printf("\n");
}
}運(yùn)行結(jié)果:
1357
2468
10203040
注意程序中的表達(dá)式*(*(p+i)+j)還可以表示成p[i][j]和(*(p+i))[j]。使用多維數(shù)組時(shí)一定要注意類型問題,下面以二維數(shù)組為例來說明使用多維數(shù)組作函數(shù)參數(shù)時(shí)應(yīng)注意的問題。
(1)形參說明為指向數(shù)組元素的指針,實(shí)參為數(shù)組元素的地址或指向元素的指針。例如:調(diào)用函數(shù)f(),用數(shù)組元素的地址作實(shí)參:
inta[2][3];
voidf(int*,int);
f(a[0],2*3);
a[0]是元素a[0][0]的地址,f(a[0],2*3)調(diào)用也可以寫成f(&a[0][0],2*3)。2*3是元素的個(gè)數(shù)。調(diào)用函數(shù)f(),用指向數(shù)組元素的指針作實(shí)參:
inta[2][3],*pi;
voidf(int*,int);
pi=a[0];/*或pi=&a[0][0]*/
f(pi,2*3);
pi是指向元素a[0][0]的指針。函數(shù)f()的定義:
voidf(int*pi,intsize)
{
}形參pi說明為列指針。
(2)形參說明為行指針,實(shí)參為行地址或行指針。例如,調(diào)用函數(shù)f(),用行指針作實(shí)參:
inta[2][3];
voidf(int(*)[3],int);
f(a,2);
實(shí)參a是行指針,類型為int(*)[3];實(shí)參2是二維數(shù)組a的行數(shù)。調(diào)用函數(shù)f(),用行指針作實(shí)參:
inta[2][3],(*pa)[3];
voidf(int(*)[3],int);
pa=a;
f(pa,2);
pa是行指針,賦值語句“pa=a;”使pa指向a的第0行,pa的類型與a的類型相同。函數(shù)f()的定義:
voidf(int(*pa)[3],intsize)
{
}例8.15
行指針作函數(shù)參數(shù)。輸入一個(gè)用年、月、日表示的日期,定義函數(shù)day_of_year()將它轉(zhuǎn)換成該年的第幾天;輸入某年的第幾天,定義函數(shù)month_day()將它轉(zhuǎn)換成該年的某月某日。
#include<stdio.h>/*day_of_year:從月份和日期計(jì)算為一年中的第幾天*/
intday_of_year(intyear,intmonth,intday,int*pi)
{
inti,leap;
leap=year%4==0&&year%100!=0||year%400==0;
for(i=1;i<month;i++)
day+=*(pi+leap*13+i);
return(day);
}/*month_day:從一年中的第幾天計(jì)算月份和日期*/
voidmonth_day(intyear,intyday,int(*pdaytab)[13],int*pmonth,int*pday)
{
inti,leap;
leap=year%4==0&&year%100!=0||year%400==0;
for(i=1;yday>*(*(pdaytab+leap)+i);i++)
yday-=*(*(pdaytab+leap)+i);*pmonth=i;*pday=yday;
}
intmain(void)
{
intdaytab[2][13]={
{0,31,28,31,30,31,30,31,31,30,31,30,31},
{0,31,29,31,30,31,30,31,31,30,31,30,31}
},y,m,d,yd;
printf("inputyear,month,day:\n");
scanf("%d%d%d",&y,&m,&d);
yd=day_of_year(y,m,d,&daytab[0][0]);
printf("dayofyearis%d\n",yd);
printf("inputyear,day_of_year:\n");
scanf("%d%d",&y,&yd);
month_day(y,yd,daytab,&m,&d);
printf("%d,%din%d\n",m,d,y);
return0;
}運(yùn)行結(jié)果:
inputyear,month,day: (輸出)
19951211↙ (輸入)
dayofyearis345 (輸出)
inputyear,day_of_year: (輸出)
1994280↙ (輸入)
10,7in1994 (輸出)
day_of_year()函數(shù)的返回值為轉(zhuǎn)換后的該年第幾天;形參year、month和day是輸入的年、月、日信息;pi是指向一個(gè)整型變量的指針,調(diào)用時(shí)傳給pi的是數(shù)組daytab的首地址。局部變量leap根據(jù)年號year是平年還是閏年分別置為0和1,leap起著daytab數(shù)組行下標(biāo)的作用:如果year是平年,leap為0,計(jì)算時(shí)使用第0行元素;否則leap為1,使用第1行元素。*(pi+leap*13+i)根據(jù)leap的值引用第0行的第i個(gè)元素或第1行的第i個(gè)元素,其中l(wèi)eap*13是當(dāng)year為閏年時(shí)指針越過第0行元素。day的初值是調(diào)用時(shí)傳給它的某月的天數(shù),循環(huán)共執(zhí)行(month-l)次,使day的累加和為年號year的第day天。因而day就是函數(shù)day_of_year()的返回值。函數(shù)month_day()無返回值。形參year和day是輸入的年號和該年第幾天的信息。
pdaytab是指向含有13個(gè)整型元素的數(shù)組的指針,調(diào)用時(shí)傳給它的是daytab數(shù)組第0行的首地址,即*pdaytab或*(pdaytab+0)是指向第0行的指針,當(dāng)year為閏年時(shí)(leap為1),*(pdaytab+leap)越過第0行,指向第1行的第0個(gè)元素;形參pmonth和pday是指向整型變量的指針,調(diào)用時(shí)傳給pmonth和pday的分別是變量m和d的地址,返回時(shí)變量m和d分別為轉(zhuǎn)換結(jié)果月和日。8.3.5指針數(shù)組指針變量可以同其它變量一樣作為數(shù)組的元素,由指針變量組成的數(shù)組稱為指針數(shù)組,組成數(shù)組的每個(gè)元素都是相同類型的指針。指針數(shù)組說明的形式為:
類型名*數(shù)組名[常量表達(dá)式]:其中“*數(shù)組名[常量表達(dá)式]”是指針數(shù)組說明符。例如:
int*ps[10];說明:ps是含有10個(gè)元素的指針數(shù)組,每個(gè)元素是一個(gè)指向int型變量的指針。注意:int*ps[10]不同于int(*ps)[10],后者說明ps是一個(gè)指向有10個(gè)int型元素的數(shù)組的指針。因?yàn)椋郏莸膬?yōu)先級高于*,所以int*ps[10]的解釋過程為:ps是一個(gè)數(shù)組,它含有10個(gè)元素;每個(gè)元素是一個(gè)int指針。指針數(shù)組可以與其它同類型對象在一個(gè)說明語句中說明。例如:
charc,*pc,*name[5];
floatx,*px[5];c是一個(gè)字符變量;pc是一個(gè)字符指針;name是含有5個(gè)元素的指針數(shù)組,每個(gè)元素是一個(gè)字符指針。x是一個(gè)float變量;px是含有5個(gè)元素的指針數(shù)組,每個(gè)元素是一個(gè)float指針。指針數(shù)組的主要用途是表示二維數(shù)組,尤其是表示字符串的數(shù)組。用指針數(shù)組表示二維數(shù)組的優(yōu)點(diǎn)是:每一個(gè)字符串可以具有不同的長度。用指針數(shù)組表示字符串?dāng)?shù)組處理起來十分方便靈活。設(shè)有二維數(shù)組說明:
inta[4][4];用指針數(shù)組表示數(shù)組a,就是把a(bǔ)看成4個(gè)一維數(shù)組,并說明一個(gè)有4個(gè)元素的指針數(shù)組pa,用于集中存放a的每一行元素的首地址,且使指針數(shù)組的每個(gè)元素pa[i]指向a的相應(yīng)行。于是可以用指針數(shù)組名pa或指針數(shù)組元素pa[i]引用數(shù)組a的元素。指針數(shù)組pa的說明和賦值如下:
int*pa[4],a[4][4];
pa[0]=&a[0][0];或pa[0]=a[0];
pa[1]=&a[1][0];或pa[1]=a[1];
pa[2]=&a[2][0];或pa[2]=a[2];
pa[3]=&a[3][0];或pa[3]=a[3];*(*(pa+i)+0),*pa[i]或*(pa[i]+0)(i=0,1,2,3)引用第i行第0列元素a[i][0];*(*(pa+i)+1)或*(pa[i]+1)引用第i行第1列元素a[i][1];…。用指針數(shù)組表示二維數(shù)組在效果上與數(shù)組的下標(biāo)表示是相同的,只是表示形式不同。用指針數(shù)組表示時(shí),需要額外增加用作指針的存儲開銷;但用指針方式存取數(shù)組元素比用下標(biāo)速度快,而且每個(gè)指針?biāo)赶虻臄?shù)組元素的個(gè)數(shù)可以不相同。例如,可用有5個(gè)元素的指針數(shù)組和5個(gè)不同長度的整型數(shù)組來描述下面的三角矩陣:
a00
a10
a11
a20
a21
a22
a30
a31
a32
a33
a40
a41
a42
a43
a44存儲三角矩陣的數(shù)組和每一行的指針可說明如下:
inta1[1],a2[2],a3[3],a4[4],a5[5],*pa[5];下面的語句使pa的每個(gè)元素指向三角矩陣的每一行:
pa[1]=&a1[0];
pa[2]=&a2[0];
pa[3]=&a3[0];
pa[4]=&a4[0];
pa[5]=&a5[0];對照前面所講二維數(shù)組的指針表示可以看出,用指針數(shù)組表示二維數(shù)組其實(shí)質(zhì)就是用指針表示二維數(shù)組,只是用指針數(shù)組更直觀、更方便而已(主要體現(xiàn)在處理字符串?dāng)?shù)組上)。用指針數(shù)組表示二維數(shù)組的方法可以推廣到三維以上數(shù)組。例如,對三維數(shù)組來說,指針數(shù)組元素的個(gè)數(shù)應(yīng)與左邊第一維的長度相同,指針數(shù)組的每個(gè)元素指向的是一個(gè)二維數(shù)組,而且每個(gè)二維數(shù)組的大小也可以不同。指針數(shù)組不經(jīng)常用于描述整型、浮點(diǎn)型等多維數(shù)組,用得最多的是描述由不同長度的字符串組成的數(shù)組。8.4指針與函數(shù)指針除了可以作為函數(shù)參數(shù)外,它與函數(shù)本身還有兩方面的關(guān)系。一方面,對于指針來說,它可以是指向函數(shù)的,也就是說,指針存放的是函數(shù)的入口地址。另一方面,對于函數(shù)來說,它的返回值可以是一個(gè)地址,我們可以將該函數(shù)的返回值定義為某種類型的指針。8.4.1指向函數(shù)的指針對于函數(shù)和數(shù)組來說,可以通過函數(shù)名和數(shù)組名來訪問它們,也可以通過指向它們的指針來訪問,這一點(diǎn)是類似的。
C語言可以定義指向函數(shù)的指針,函數(shù)型指針的定義形式為:類型標(biāo)識符(*指針名)();例如:
int(*fp)();說明:fp是指向int類型函數(shù)的指針。與指向數(shù)組的指針說明類似,說明符中用于改變運(yùn)算順序的()不能省。如果將(*fp)()寫成*fp(),則fp成為返回值為指針類型的函數(shù)。指向函數(shù)的指針是存放函數(shù)入口地址的變量,一個(gè)函數(shù)的入口地址由函數(shù)名表示,它是函數(shù)體內(nèi)第一個(gè)可執(zhí)行語句的代碼在內(nèi)存中的地址。如果把函數(shù)名賦給一個(gè)指向函數(shù)的指針,就可以用該函數(shù)型指針來調(diào)用函數(shù)。函數(shù)型指針可以被賦值,可以作為數(shù)組的元素,可以傳給函數(shù),也可以作為函數(shù)的返回值。其中常用的是將函數(shù)名傳給另一函數(shù)。C語言允許將函數(shù)的名字作為函數(shù)參數(shù)傳給另一函數(shù),由于參數(shù)傳遞是單向值傳遞,相當(dāng)于將函數(shù)名賦給形參,因此在被調(diào)用函數(shù)中,接收函數(shù)名的形參是指向函數(shù)的指針。被調(diào)用函數(shù)可以通過函數(shù)的指針來調(diào)用完成不同功能的具體函數(shù)。下面的簡單例子說明函數(shù)指針的用法。
例8.16
指向函數(shù)的指針。設(shè)一個(gè)函數(shù)operate(),在調(diào)用它的時(shí)候,每次實(shí)現(xiàn)不同的功能。輸入a和b兩個(gè)數(shù),第一次調(diào)用operate()得到a和b中最大值,第二次得到最小值,第三次得到a與b之和。
#include<stdio.h>
voidmain()
{
voidoperate();
intmax(),min(),sum(),a,b;
/*必須進(jìn)行函數(shù)聲明,否則無法調(diào)用*/
printf("Entertwonumber:");
scanf("%d%d",&a,&b);
printf("max=");operate(a,b,max);
printf("min=");operate(a,b,min);
printf("sum=");operate(a,b,sum);
}
intmax(intx,inty)
{
if(x>y)return(x);
elsereturn(y);
}
intmin(intx,inty)
{
if(x<y)return(x);
elsereturn(y);
}
intsum(intx,inty)
{
return(x+y);
}
voidoperate(intx,inty,int(*fun)())
{
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年分離純化控制系統(tǒng)合作協(xié)議書
- 人教版 八年級英語下冊 Unit 10 單元綜合測試卷(2025年春)
- 人教版化學(xué)九年級上冊第一單元《-走進(jìn)化學(xué)世界》測試試題(含答案)
- 2025年產(chǎn)品買賣協(xié)議常用版(4篇)
- 2025年個(gè)人車輛出租合同常用版(4篇)
- 2025年代理進(jìn)口合同標(biāo)準(zhǔn)范文(2篇)
- 2025年九年級年級組長管理工作總結(jié)(四篇)
- 2025年人防工程施工合同(三篇)
- 2025年個(gè)人股權(quán)的投資協(xié)議(三篇)
- 2025年九年級班主任年度期末工作總結(jié)模版(二篇)
- 產(chǎn)業(yè)鏈競爭關(guān)聯(lián)度
- TTJSFB 002-2024 綠色融資租賃項(xiàng)目評價(jià)指南
- 涵洞施工鋼筋混凝土圓管涵
- 高考地理一輪復(fù)習(xí)學(xué)案+區(qū)域地理填圖+亞洲
- 全新車位轉(zhuǎn)讓協(xié)議模板下載(2024版)
- 高中數(shù)學(xué)必修一試卷及答案
- 砌筑工考試卷及答案
- 呼吸治療師進(jìn)修匯報(bào)
- 智慧港口和自動(dòng)化集裝箱碼頭
- 2024年江西電力職業(yè)技術(shù)學(xué)院單招職業(yè)技能測試題庫及答案解析
- 天合儲能:2024儲能專用電芯白皮書
評論
0/150
提交評論