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

下載本文檔

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

文檔簡(jiǎn)介

第5章指針、數(shù)組和結(jié)構(gòu)

5.1指針5.2數(shù)組5.3指向數(shù)組的指針5.4指向函數(shù)的指針5.5指向void*的指針5.6常量5.7引用5.8結(jié)構(gòu)小結(jié)練習(xí)題

5.1指針

5.1.1指針與指針變量

所謂指針,即一個(gè)變量的內(nèi)存存儲(chǔ)地址。假設(shè)變量v的地址是0x06AF(十六進(jìn)制),我們就說(shuō)v的指針(或稱指針值)是0x06AF。

對(duì)于類型T(T為基本類型或?yàn)樽远x類型),指針類型(表示為T(mén)*)是“指向T類型對(duì)象的指針”,即一個(gè)T*?類型的指針變量只能存放一個(gè)類型為T(mén)的對(duì)象的地址。例如:

charc=‘a(chǎn)’;圖5.1char*類型的變量pv中存放char類型變量c的地址

char*pv=&c;//取變量c的地址,并賦給指針char*類型的變量pv用圖5.1表示如下。引入指針類型的目的是用某個(gè)指針變量中的值間接地存/取它所指的對(duì)象的內(nèi)容。

大部分教科書(shū)及專業(yè)書(shū)籍都把指針和指針變量統(tǒng)稱為指針,因此,到底是指針還是指針變量,讀者在學(xué)習(xí)與閱讀時(shí)一定要注意其上下文環(huán)境,并根據(jù)上下文來(lái)進(jìn)行判斷。5.1.2為什么要使用指針變量

通常,我們是用某種類型的變量去存儲(chǔ)相應(yīng)類型的值,然后,通過(guò)變量名去存/取(注意:計(jì)算機(jī)的動(dòng)作是在內(nèi)存中存/取)其中的值。既然能用變量名(與某塊內(nèi)存綁定)直接存/取內(nèi)存中的值,那么為什么還要用指針來(lái)間接存/取內(nèi)存中所存儲(chǔ)的值呢?

應(yīng)用中,以下幾種情況必須使用指針:

(1)如果在起始地址為Av的存儲(chǔ)空間Sv中存儲(chǔ)的值是在運(yùn)行時(shí)根據(jù)運(yùn)行情況才寫(xiě)入的,不能事先規(guī)定所寫(xiě)入的是哪個(gè)變量的值,能夠保證的只是所寫(xiě)入的一定是類型為T(mén)的值,這時(shí)應(yīng)該采用指針。例如,多進(jìn)程之間利用一塊共享內(nèi)存進(jìn)行通信時(shí),其底層實(shí)現(xiàn)采用的就是指針,如圖5.2所示。圖5.2多進(jìn)程之間的通信

(2)如果需要同時(shí)對(duì)一組同類型的數(shù)據(jù)進(jìn)行多個(gè)側(cè)面或角度的組織,以有效地支持多種不同性質(zhì)的操作(源數(shù)據(jù)要求不動(dòng)),這時(shí)亦應(yīng)該使用指針。例如,某應(yīng)用欲實(shí)現(xiàn)一批整型數(shù)同時(shí)進(jìn)行遞增和遞減排序,并要求源數(shù)據(jù)保持不動(dòng),這時(shí)就需要采用指針,如圖5.3所示。圖5.3源數(shù)據(jù)要求不動(dòng)的一批同類型數(shù)據(jù)的遞增、遞減排序

(3)對(duì)于連續(xù)存儲(chǔ)著類型為T(mén)的許多個(gè)值(例如數(shù)組)的情況,當(dāng)需要依次進(jìn)行某種處理時(shí),可以在不需要知道數(shù)組下標(biāo)(或數(shù)組的界)的情況下,用改變一個(gè)指針變量的值的方式來(lái)依次進(jìn)行這一批同類型量的訪問(wèn),如圖5.4所示。

此外,指針還有其它的一些應(yīng)用場(chǎng)合,此處不一一介紹。圖5.4用指針對(duì)一批連續(xù)存放的同類型值的處理5.1.3指針變量的聲明與定義

與指針相關(guān)的聲明共分兩類,即定義聲明和非定義聲明。

1.定義聲明

(1)定義聲明與數(shù)據(jù)相關(guān)的指針,例如:

char*pc; //pc:指向char類型值的指針變量

int*pi; //pi:指向int類型值的指針變量

char**ppc; //ppc:指向“指向char類型值的指針”的

指針變量

int*ap[15]; //ap:由15個(gè)指向int類型值的指針變量

構(gòu)成的指針數(shù)組

(2)定義聲明與函數(shù)相關(guān)的指針,例如:

int(*fp)(char*);

//fp:指向參數(shù)為char*類型、返回值為int類型的函數(shù)指針

2.非定義聲明

例如:

int*f(char*); //參數(shù)為char*類型、返回值為int*類型

的函數(shù)聲明

C++的語(yǔ)法并不限定多級(jí)指針的定義,例如我們可定義一個(gè)二級(jí)指針:

char**ppc;

則ppc指針變量中就可存放指向char類型的指針變量的地址,如圖5.5所示。圖5.5二級(jí)指針變量中的內(nèi)容我們亦可定義一個(gè)三級(jí)指針(或更多級(jí)的指針),例如:

int***ppi;

但實(shí)際應(yīng)用中,應(yīng)避免定義復(fù)雜的多級(jí)指針,二級(jí)指針變量足矣!5.1.4指針變量的操作

設(shè)T為任意類型(基本類型或用戶自定義類型),則T類型指針變量的類型為T(mén)*。與指針類型相關(guān)的操作如下。

1.取地址

操作符:&,一元前綴運(yùn)算符,采用右結(jié)合律。

語(yǔ)義:取某個(gè)T類型變量的地址并返回之。

例如:

charc=‘a(chǎn)’;

char*p=&c;//取c變量的地址并賦給指針變量p

2.求內(nèi)容(內(nèi)容解析)

操作符:*,一元前綴運(yùn)算符,采用右結(jié)合律。

語(yǔ)義:以相應(yīng)指針變量中的值為地址,求出該地址中存儲(chǔ)的值并返回之。

例如:

charc=‘a(chǎn)’;

char*p=&c;

charc2=*p; //c2=‘a(chǎn)’

注意:&

*

二者互為逆操作。若定義

charc;

char*p=&c;

則*(&c)==c,&(*p)==p。

3.指針變量的自增(減)操作

指針變量可進(jìn)行前綴/后綴的自增(減)操作。例如:

charc=‘a(chǎn)’;

char*pc=&a;

pc++;

//等價(jià)于pc+sizeof(char);

--pc;//等價(jià)于pc-sizeof(char);

簡(jiǎn)言之,對(duì)于一個(gè)T*類型的指針變量pt:

pt++; //等價(jià)于pt=pt+sizeof(T);

++pt; //等價(jià)于pt=pt+sizeof(T);

pt--;//等價(jià)于pt=pt-sizeof(T);

--pt;//等價(jià)于pt=pt-sizeof(T);

4.指針變量的加(減)操作

指針變量可與一個(gè)整數(shù)常量進(jìn)行加、減操作。設(shè)N為一整型常量,則

pt+N; //等價(jià)于pt=pt+N*sizeof(T)

pt-N;//等價(jià)于pt=pt-N*sizeof(T)

5.指向同一數(shù)組的指針變量的相減操作

當(dāng)兩個(gè)同類型的指針變量指向同一數(shù)組時(shí),兩指針變量可進(jìn)行相減操作。相加操作沒(méi)有定義,被認(rèn)為是非法操作。例如:

inta[]={1,2,3,4,5,6};

int*pa1,pa2;

pa1=a; //pa1指向數(shù)組首元素a[0]

pa2=&a[4]; //pa2指向數(shù)組元素a[4]

intn=pa2-pa1;//n=4,表示二指針?biāo)冈匚恢弥g

的差

n=pa1+pa2;//錯(cuò)誤!沒(méi)有定義

由于指針變量的自增(減)操作、加(減)操作是直接針對(duì)內(nèi)存地址的操作,所以在進(jìn)行這些操作時(shí),一定要注意指針的越界問(wèn)題。關(guān)于此問(wèn)題的討論與處理詳見(jiàn)下文。

T*類型指針變量的取值范圍為T(mén)類型變量的所有的合法地址。5.1.5常量零(0)

一般而言,數(shù)字字面值0的類型為整型。由于C++中允許各種基本類型的相互轉(zhuǎn)換,因此,數(shù)字0廣義地說(shuō),可作為任意整型、浮點(diǎn)型、指針、指向成員的指針變量的字面值常量。因此,在上述情況下,數(shù)學(xué)0的類型就與上下文環(huán)境相關(guān),并由其上下文環(huán)境所決定。

因計(jì)算機(jī)內(nèi)存地址為0的單元一直保留未用,即沒(méi)有任何對(duì)象會(huì)被分配到內(nèi)存地址為0的地方,故0可用作一個(gè)指針變量的字面值常量。當(dāng)一個(gè)指針變量初始化為0時(shí),表示它此刻沒(méi)有指向任何對(duì)象。在C中流行用宏NULL表示0。由于C++不同的實(shí)現(xiàn)可能對(duì)NULL的定義不同,所以,直接采用0而非NULL會(huì)使得指針的初始化更可靠、安全。如果你非常熟悉C,且習(xí)慣于采用C的宏NULL,建議在編寫(xiě)C++程序時(shí)可先定義:

constintNULL=0;//定義NULL是一個(gè)表示0的字符常量

然后在程序中再采用符號(hào)常量NULL。

5.2數(shù)組

對(duì)于類型T,一個(gè)數(shù)組類型T[size](size表示數(shù)組中元素的個(gè)數(shù),一般為整型常量)是類型為T(mén)的size個(gè)元素的有序集合,數(shù)組中的元素可用下標(biāo)訪問(wèn),其下標(biāo)范圍為[0~size-1]。

數(shù)組類型屬于構(gòu)造類型,即一個(gè)數(shù)組類型是由兩個(gè)類型構(gòu)造而成的:一個(gè)是數(shù)組元素的類型(如T類型),另一個(gè)是索引(或稱下標(biāo))類型(如{0,1,…,size-1},即對(duì)應(yīng)索引類型的值集)。標(biāo)準(zhǔn)C++規(guī)定:數(shù)組元素的類型可為任意數(shù)據(jù)類型(包括另一個(gè)數(shù)組類型,但必須保證其元素的類型是同質(zhì)的),而索引(下標(biāo))類型要求其值集上的元素要有偏序(順序)關(guān)系(一般為整型或枚舉類型)。5.2.1數(shù)組的定義與初始化

在C++中,可定義一維數(shù)組和多維數(shù)組,定義方式如下例所示:

inta[10];

//定義一個(gè)元素類型為int,元素個(gè)數(shù)為

10的一維整型數(shù)組a

intb[2][3]; //定義一個(gè)元素類型為int,元素個(gè)數(shù)

(2行3列)為6的二維數(shù)組b

定義數(shù)組時(shí),其數(shù)組類型和數(shù)組類型的變量(后者通常簡(jiǎn)稱為數(shù)組)一般在定義聲明中是同時(shí)聲明的。例:

floatv[3]; //float[3]v;

char*a[32]; //(char*)[32]a;

intd2[10][7]; //(int[10])[7]d2;

上述第一條語(yǔ)句定義了一個(gè)元素類型為float,元素個(gè)數(shù)為3的數(shù)組類型,并定義了這種數(shù)組類型的一個(gè)變量為v,數(shù)組類型與其類型的變量二者在定義時(shí)同時(shí)聲明。

第二條語(yǔ)句定義了一個(gè)元素類型為char*,元素個(gè)數(shù)為32的數(shù)組類型(即字符指針數(shù)組),并定義了該數(shù)組類型的一個(gè)變量a,數(shù)組類型與其類型的變量二者在定義時(shí)同時(shí)聲明。第三條語(yǔ)句定義了一個(gè)元素類型為int[10](一維整型數(shù)組,元素個(gè)數(shù)為10),元素個(gè)數(shù)為7的數(shù)組類型(即數(shù)組中的數(shù)組),并定義了該數(shù)組類型的變量d2,數(shù)組類型與其類型的變量d2二者在定義時(shí)同時(shí)聲明。

一旦定義聲明了某個(gè)數(shù)組類型T[size](size為常量)的變量a,編譯器在編譯時(shí)會(huì)根據(jù)數(shù)組的定義給這個(gè)變量a連續(xù)分配一塊大小為sizeof(T)*size的內(nèi)存空間,并將這塊內(nèi)存空間的首地址裝入數(shù)組名中。因此,C++中的數(shù)組是靜態(tài)的并具有固定尺寸的一種數(shù)據(jù)結(jié)構(gòu),它常用于對(duì)一批同類型量的組織與操作。

C++只允許定義大小固定的靜態(tài)、矩形的一維或多維數(shù)組(注意:數(shù)組尺寸不允許在運(yùn)行時(shí)改變)。若需要?jiǎng)討B(tài)數(shù)組(即數(shù)組尺寸在運(yùn)行時(shí)可變),請(qǐng)用C++標(biāo)準(zhǔn)類庫(kù)中的vector類(vector類的詳細(xì)內(nèi)容見(jiàn)本書(shū)第三部分)。

定義數(shù)組時(shí),數(shù)組類型定義中的size(數(shù)組元素的個(gè)數(shù)或稱數(shù)組的尺寸)可以不給出,而交由編譯程序推斷出來(lái),但此時(shí)必須在定義聲明中對(duì)數(shù)組進(jìn)行初始化,初始值的個(gè)數(shù)即為數(shù)組的size。例如:

int v1[]={1,2,3,4};//int[4]v1;

char v2[]={‘a(chǎn)’,‘b’,‘c’,0};

//char[4]v2;

int i,j,k;

int* v3[]={&i,&j,&k,0};//int*[4]v3;

在定義多維數(shù)組時(shí),語(yǔ)法上規(guī)定:其第一維的大小可以不給出,但除第一維以外的維數(shù)大小在定義時(shí)一定要給出。因?yàn)镃++對(duì)多維數(shù)組的內(nèi)存分配仍然是一塊連續(xù)的內(nèi)存空間,但C++是“按行”進(jìn)行內(nèi)存分配的(某些語(yǔ)言是“按列”進(jìn)行分配的,如Fortran語(yǔ)言)。以二維數(shù)組為例,若不指定第二維的尺寸,編譯器將無(wú)法推斷和檢查所分配的內(nèi)存。例如:

float f[][3]={{1,2,3},{4,5,6}}; //正確!編譯器推斷為2行3列的float型數(shù)組

int a[][4]={1,2,3,4,5,6,7,8}; //正確!編譯器推斷為2行4列的int型數(shù)組

double d[2][]={2,4,6,8,10}; //錯(cuò)誤!編譯器無(wú)法推斷內(nèi)存分配量

數(shù)組類型定義時(shí)可給出size(整數(shù)常量/整型常量表達(dá)式),進(jìn)而在定義聲明中對(duì)數(shù)組進(jìn)行初始化;初始化的元素個(gè)數(shù)不能大于size,但可以小于size(C++自動(dòng)地對(duì)未給出初值的元素賦0)。例如:

int?v1[4]={1,2,3,4}; //正確!int[4]v1;

charv2[2]={‘a(chǎn)’,‘b’,‘c’,0}; //錯(cuò)誤!初始值的個(gè)數(shù)4大于數(shù)組尺寸2

inti,j,k;

int*v3[8]={&i,&j,&k,0}; //int*[8]v3;

//相當(dāng)于int*v3[8]={&i,&j,&k,0,0,0,0,0};如前所述,字面值0在不同的上下文環(huán)境中具有不同的類型,而不同類型的0在不同的上下文環(huán)境中又有不同的語(yǔ)義。例如,0對(duì)于整型而言,就是0;對(duì)于指針T*而言,它表示空指針;對(duì)于字符串char*而言,它是串結(jié)束符。因此,采用系統(tǒng)默認(rèn)的0賦值,在某些情況下可能偏離編程者的本意,有時(shí)是十分危險(xiǎn)的!

在C++中,對(duì)于全局或靜態(tài)的(static)數(shù)組變量,當(dāng)用戶不賦初值時(shí),系統(tǒng)自動(dòng)賦初值0,局部數(shù)組系統(tǒng)不賦初值。5.2.2字符串字面值

由于應(yīng)用中常常采用char*的變量或char[]數(shù)組變量存儲(chǔ)一字符串常量,故在此引入字符串字面值的概念。

一個(gè)字符串字面值是由一對(duì)雙引號(hào)括起來(lái)的一串字符。例如,“Iamastudent.”?就是一個(gè)合法的C++字符串字面值。

一個(gè)字符串字面值的長(zhǎng)度等于其中所包含的字符個(gè)數(shù)加1(串結(jié)束符‘\0’);每個(gè)字符串字面值以字符‘\0’(值等于0)作為串結(jié)束符。因此有:

sizeof(“Teacher”)=8個(gè)字節(jié)//假定某平臺(tái)上sizeof(char)=1字符串字面值的類型為具有確定數(shù)目的const字符數(shù)組,即為constchar[]類型(注意:用const定義(或稱約束)的量只能讀而不能寫(xiě))。

為了繼承和兼容大量原有的C/C++代碼,C++標(biāo)準(zhǔn)規(guī)定:一個(gè)字符串字面值可以賦給一個(gè)char*的變量(在原有的C/C++定義中,字符串字面值的類型為char*)。由于C++標(biāo)準(zhǔn)規(guī)定字符串字面值是constchar[]類型,所以對(duì)字符串字面值只能讀而不能寫(xiě)。例如:

char*pc=“Teacher”;

*pc='B'; //錯(cuò)誤!試圖修改常量的值欲修改字符串字面值中的字符,可采取如下方式:

charc[]=“Teacher”;//數(shù)組的尺寸為sizeof(Teachar)+1=8

//一個(gè)常量字符串字面值賦值拷貝到一個(gè)字符數(shù)組變量中

c[0]=‘B’;//將?“Teacher”?中的首字符?‘T’?改為?‘B’

系統(tǒng)為字符串字面值靜態(tài)地分配了一塊連續(xù)的內(nèi)存空間以存放之。程序中若存在兩個(gè)或兩個(gè)以上相同的字符串字面值,系統(tǒng)是在內(nèi)存中存放該字符串的一個(gè)副本還是存放該字符串的多個(gè)副本將依具體的實(shí)現(xiàn)而定。字符串字面值中可以有帶轉(zhuǎn)義符的字符(EscapeCharacter),如‘\n’(換行)。在程序中,一個(gè)字符串字面值不能跨行定義。

字符串字面值中若有字符?‘?“?’?或?‘?’?‘?或?’\‘,要前加轉(zhuǎn)義符?’\‘。

在程序中可以將一個(gè)字符串字面值寫(xiě)成用whitespace分隔開(kāi)的多個(gè)字符串字面值,C++會(huì)將它們自動(dòng)接續(xù)成一個(gè)字符串字面值,這對(duì)在程序中書(shū)寫(xiě)較長(zhǎng)的字符串字面值提供了方便。whitespace包括:space(空格)、tab、newline(新行)、formfeed(走紙)、carriagereturn(回車)。例如:

chara1[]=“Thisisa\”string\“.\n*******************\n”;

//字符串字面值在程序中不允許跨行定義

chara2[]=“Thisisa\”string\“.

*******************”; //語(yǔ)法錯(cuò)誤!

//用whitespace分隔開(kāi)的字符串字面值將被自動(dòng)續(xù)接

chara3[]=“Thisisa\”string\“.\n”

“*******************\n”;

//定義效果與a1相同

5.3指向數(shù)組的指針

5.3.1指向一維數(shù)組的指針

指針與數(shù)組有著密切的關(guān)系。數(shù)組名本身就是一個(gè)指針,它代表數(shù)組元素的首地址,因此,我們常常利用指向數(shù)組的指針變量操作數(shù)組中的元素。

值得注意的是,程序中數(shù)組名擔(dān)當(dāng)著如下兩種角色:

(1)代表某個(gè)數(shù)組類型的變量名。因此,我們可用數(shù)組名求其數(shù)組的存儲(chǔ)空間分配量。例如:上述結(jié)果是在VC.net2005下運(yùn)行的,在此平臺(tái)上,sizeof(int)=4。因a數(shù)組的元素個(gè)數(shù)為4,故其存儲(chǔ)分配量為4×4=16。

(2)代表著存放該數(shù)組元素的首地址,注意:數(shù)組名是一個(gè)常量。因此,我們可以將數(shù)組名賦給一個(gè)與其數(shù)組元素類型一致的指針變量,之后,用其指針變量操作數(shù)組中的元素。語(yǔ)法上數(shù)組指針變量不拒絕指向數(shù)組以外的元素,但其后果自負(fù)。例如:

intv[]={1,2,3,4}; //int[4],其元素為v[0]~v[3]

int*p1=v; //被隱式轉(zhuǎn)換成int*p1=&v[0];

int*p2=&v[0];

int*p3=&v[4];//允許指針指向數(shù)組以外的元素,后果自負(fù)!

在C++中,char[](字符類型的數(shù)組)類型和char*(指向字符的指針類型)類型不同,但允許char[]到char*的轉(zhuǎn)換。對(duì)于字符數(shù)組而言,將數(shù)組名隱式轉(zhuǎn)換成字符指針?lè)奖阌谑褂脴?biāo)準(zhǔn)庫(kù)中的庫(kù)函數(shù)。char*到char[]的轉(zhuǎn)換被認(rèn)為是違法的。該類型轉(zhuǎn)換規(guī)則保證了不允許將一個(gè)char*值賦給數(shù)組名(常量)。例如:

intstrlen(constchar*);

//strlen:C標(biāo)準(zhǔn)庫(kù)函數(shù),在<cstring>中定義,求char*所指的字符串的長(zhǎng)度

voidf()

{

charv[]=“Annemarie”;

char*p=v; //隱式類型轉(zhuǎn)換,char[]→char*

intn1=strlen(p);

//函數(shù)調(diào)用時(shí)類型匹配

intn2=strlen(v);

//隱式類型轉(zhuǎn)換,char[]→char*

v=p;

//錯(cuò)誤!不允許char*→char[]以保證

數(shù)組名常量不被修改

}5.3.2指向多維數(shù)組的指針

C++的多維數(shù)組被看成是數(shù)組中的數(shù)組。以二維數(shù)組為例,C++將二維數(shù)組定義為其元素類型為一維數(shù)組的數(shù)組。應(yīng)用中,一般較少使用二維以上的數(shù)組。在此以二維數(shù)組示例,指向多維數(shù)組的指針概念和用法與二維數(shù)組類同。

在二維數(shù)組中,數(shù)組名仍然代表著數(shù)組的首地址,但它是一個(gè)行地址。例如:

inta[2][3]={1,2,3,4,5,6};

int(*pa)[3]; //pa定義為指向一個(gè)尺寸為3的一維數(shù)組的行指針

pa=a+1;

/*

pa=a+1等價(jià)于pa=&a[0][0]+3*sizeof(int),

即pa指向a數(shù)組中第二行首元素

*/

int*pa1

pa1=a[0]+1;

//pa1=&a[0][0],a[0]是數(shù)組a第一行的首地址,它為一列地址,pa1=a[0]+1=&a[0][1]

所謂行指針,即指針加1跳一行(所指的)數(shù)組元素,列指針加1跳一個(gè)(所指的)數(shù)組元素。

在上例中,數(shù)組名為行地址,而a[0](其地址等于&a[0][0])和a[1](其地址等于&a[1][0])分別為列地址。行地址的定義形式如下:數(shù)組元素類型T(*行指針名)[size];

該語(yǔ)句定義聲明了一個(gè)指向T[size]類型的指針類型,其中size為一維數(shù)組的大小。

行、列指針以上例代碼為例,其進(jìn)行增減操作后的結(jié)果如圖5.6所示。

若定義了一個(gè)指向二維數(shù)組的行指針:

inta[2][3]={{1,2,3},{4,5,6}};

int(*q)[3]=a;

我們可用如圖5.7所示的形式來(lái)表示上述二維數(shù)組中的各元素。

一個(gè)行指針進(jìn)行*運(yùn)算后(如*pa)即可轉(zhuǎn)換成一個(gè)列指針。圖5.6行、列指針示意圖圖5.7用行指針q表示2行3列數(shù)組中的各元素5.3.3取數(shù)組元素及數(shù)組的遍歷

兩個(gè)同類型的(數(shù)組元素類型相同,數(shù)組大小相同)數(shù)組可進(jìn)行元素對(duì)元素的整體賦值。例如:

inta[4]={10,20,30,40},

b[4];

b=a;

//a數(shù)組元素對(duì)b數(shù)組元素依次拷貝賦值

但除此之外,對(duì)數(shù)組的所有操作只能針對(duì)其元素進(jìn)行。因此,取數(shù)組中的某個(gè)元素或在數(shù)組中進(jìn)行遍歷是其它一切數(shù)組操作的基礎(chǔ)。通常采用數(shù)組名和下標(biāo)運(yùn)算符及索引(下標(biāo))來(lái)取數(shù)組中的某個(gè)元素(如a[i])或?qū)ζ溥M(jìn)行依次遍歷;另一種方式是利用指向數(shù)組的指針加索引進(jìn)行取元素或遍歷操作,其具體用法如下例所示。

//此函數(shù)是對(duì)指針使用數(shù)組操作

voidfi(char*v)

{

for(inti=0;v[i]!=0;i++)

use(v[i]);//use假定為已定義的操作數(shù)組元素的函數(shù)

}

//此函數(shù)是對(duì)數(shù)組使用指針操作

voidfp(charv[])

{

for(char*p=v;*p!=0;p++)

use(*p);

}

在應(yīng)用中需要注意如下幾點(diǎn):

(1)在多維數(shù)組(包括二維)中進(jìn)行取元素或遍歷操作時(shí)要注意是利用行指針還是列指針。

(2)?C++的數(shù)組不具有自描述性,即數(shù)組變量中不存放其實(shí)際存放的元素個(gè)數(shù)的信息。因此,在遍歷一個(gè)數(shù)組時(shí),必須以某種方式提供它的實(shí)際元素個(gè)數(shù)(字符串?dāng)?shù)組中包含一個(gè)串結(jié)束符?‘\0’,據(jù)此可推斷出其實(shí)際的元素個(gè)數(shù)),以供操作數(shù)組和進(jìn)行數(shù)組的越界檢查。

例如:

//函數(shù)fp:對(duì)傳遞的數(shù)組進(jìn)行某種操作

voidfp(charv[],unsignedintsize) //傳遞時(shí)須同時(shí)傳遞數(shù)組其及大小

{

for(inti=0;i<size;i++)

use(v[i]);

//...

}

(3)?C++語(yǔ)法上允許數(shù)組的越界操作,并且編譯器忽略此類錯(cuò)誤。因此,對(duì)數(shù)組進(jìn)行操作時(shí),一定要注意數(shù)組的越界問(wèn)題。一個(gè)好的C/C++程序員,應(yīng)將對(duì)數(shù)組的越界檢查及處理作為其基本本能。

(4)用指針和數(shù)組形式進(jìn)行數(shù)組的操作,在許多情況下是等效的,但還是應(yīng)當(dāng)加以約束,以避免平臺(tái)不同帶來(lái)的影響。請(qǐng)看下面一實(shí)際應(yīng)用中的程序片斷:

constunsignedshortID;

//數(shù)據(jù)庫(kù)表的數(shù)據(jù)項(xiàng)描述

structTB_col{

ID col_id;

char col_name[19];

short col_type;

short col_length;

};

//取指定數(shù)據(jù)項(xiàng)的物理名

char*TB_GetColName(IDtab_id,IDcol_id)

{staticstructTB_col*cp;

if(!TB_GetColInf(tab_id,col_id,&cp))

//根據(jù)表名tab_id和列名col_id及cp指針取列的相應(yīng)信息

returnNULL;

returncp->col_name;

}

上述代碼片斷中的retuencp->col_name;?語(yǔ)句是用指針取col_name(char*類型)中的字符串(表列名)。在Windows平臺(tái)上,無(wú)論col_name中的字符串非空/空,returncp->col_name;語(yǔ)句都將如實(shí)地返回col_name中的內(nèi)容;但在UNIX平臺(tái)上,若col_name為空串,則語(yǔ)句不返回任何值,這導(dǎo)致了程序錯(cuò)誤。因此,上述的returncp->col_name;語(yǔ)句應(yīng)改為

return(cp->col_name[0]=='\0')?0:cp->col_name;

這樣將會(huì)更安全、可靠地實(shí)現(xiàn)跨平臺(tái)使用程序。下面給出對(duì)數(shù)組進(jìn)行取元素或遍歷操作的示例程序。

例1打印輸出數(shù)組中的元素值并用直方圖的形式表示之(若干*號(hào)個(gè)數(shù)的條形圖)。

(*q)[1]is2

(*q+1)[1]is3

*(*(z+1)+2)is-33

*(z[1]+2)is-33

5.4指向函數(shù)的指針

C++允許定義指向某種類型函數(shù)的指針變量。例如下述語(yǔ)句:

int(*fp)(int);

即定義了一個(gè)指向其函數(shù)返回類型為int、參數(shù)為int型的該類函數(shù)的指針變量fp。這類指針變量可存放該類函數(shù)的地址,即所定義的fp中可存放上述類別的函數(shù)的地址(或稱函數(shù)代碼的首地址)。

函數(shù)指針的定義方式如下:

函數(shù)返回類型(*函數(shù)指針名)(函數(shù)參數(shù)表列);函數(shù)指針的一個(gè)主要目的是讓用戶編寫(xiě)更為靈活、用數(shù)據(jù)實(shí)現(xiàn)控制的程序。假定應(yīng)用中我們需編寫(xiě)計(jì)算如下積分的程序:經(jīng)過(guò)分析可看出:y1、y2和y3的被積函數(shù)雖然形式不同,但具有共性,即被積函數(shù)都只有一個(gè)自變量x,且被積函數(shù)都是x的一元某次方程。根據(jù)其共性,可編寫(xiě)適合這一類被積函數(shù)的積分程序。若用函數(shù)實(shí)現(xiàn),則解決方案如下:

floatintegral(fp,a,b);//計(jì)算某類函數(shù)積分的通用函數(shù)接口

//fp為指向該類被積函數(shù)的指針,a、b為積分的上、下限

上述函數(shù)的具體實(shí)現(xiàn)請(qǐng)參閱后續(xù)函數(shù)一章的相應(yīng)內(nèi)容。

5.5指向void*的指針

若定義了一個(gè)void*的指針變量,就意味著可將一個(gè)指向任意類型對(duì)象的指針賦給它。

一個(gè)void*類型的變量所能進(jìn)行的操作是:

將一個(gè)void*類型的變量賦給另一個(gè)void*類型的變量;

兩個(gè)void*的相等與不相等比較;

一個(gè)void*可以顯式地轉(zhuǎn)換成另一個(gè)任意類型的指針類型(注意謹(jǐn)慎使用!)。

void*類型的變量不能進(jìn)行的操作是:

將函數(shù)指針賦給void*;

將指向成員的指針賦給void*。

void*最重要的用途是需要向函數(shù)傳遞一個(gè)指針,而向函數(shù)傳遞時(shí),又不能對(duì)指針?biāo)笇?duì)象的類型做任何假設(shè),即利用void*可向函數(shù)傳遞任意類型的對(duì)象。其另外一個(gè)主要用途就是從函數(shù)返回一個(gè)無(wú)類型(Untype,即可為任意類型)的對(duì)象。因此,被定義成void*類型的量所提供的信息是:由于不可能事先約定,所以允許任意的指針類型與之對(duì)應(yīng)(通常是函數(shù)的形參或返回值)。這是C++為程序員提供的一種可傳遞/處理任何類型對(duì)象的機(jī)制。

這樣做的可行性在于:在同一平臺(tái)上任何指針類型的存儲(chǔ)空間分配量是一致的。在C++標(biāo)準(zhǔn)庫(kù)函數(shù)的一些實(shí)現(xiàn)中,void*機(jī)制的使用使得該函數(shù)具有較強(qiáng)的通用性,請(qǐng)看在C標(biāo)準(zhǔn)庫(kù)中的一個(gè)典型應(yīng)用:

//C標(biāo)準(zhǔn)庫(kù)函數(shù)——快速排序qsort函數(shù)的接口定義

intqsort(void*,int,intsize_t,(int(*fp)(constvoid*,constvoid*)));

/*函數(shù)中各參數(shù)的含義如下:

被排序的數(shù)組的起始地址,由于是void*?類型,則可接受任何類型的數(shù)組;

(數(shù)組中存放欲排序的數(shù)據(jù))

數(shù)組中有效元素個(gè)數(shù);數(shù)組尺寸的大小;

指定的比較函數(shù)的指針。由于函數(shù)的兩個(gè)參數(shù)的類型為void*?類型,故函數(shù)可進(jìn)行

任何類型的兩個(gè)量的比較

*/

下面我們利用C標(biāo)準(zhǔn)庫(kù)函數(shù)qsort高度抽象的接口與實(shí)現(xiàn)的定義來(lái)實(shí)現(xiàn)任意類型量的排序。

假定應(yīng)用中我們需對(duì)一批字符串進(jìn)行排序,代碼如下:

constintTABLE_SIZE=1000;

char*ourTable[TABLE_SIZE];

//…向ourTable輸入欲排序的數(shù)據(jù)

5.6常量

C++允許用戶定義字符常量(也稱符號(hào)常量)。C++用關(guān)鍵字const來(lái)定義符號(hào)常量,const用來(lái)直接表達(dá)“不變化的值”這一概念。例如,我們欲定義PI這一符號(hào)常量,可寫(xiě)為

constfloatPI=3.15159;

C++仍兼容C的常量定義方式(采用宏),例如:

#definePI3.14159

//PI為一字符常量,代表3.14159

比較C和C++的兩種常量定義方法,我們可看到:C++的常量定義方式不僅含義更加明確,最重要的是該常量定義給編譯器和運(yùn)行環(huán)境提供了被定義者是常量及所具有的類型這些重要的信息,以供編譯器和運(yùn)行環(huán)境進(jìn)行編譯時(shí)和運(yùn)行時(shí)的類型檢查!?這在應(yīng)用中是十分重要的。

(2)采用符號(hào)常量而不是將常量字面值直接寫(xiě)入代碼中,將使得程序更易于擴(kuò)充、維護(hù)與修改。例如:若需修改數(shù)組的尺寸,只需修改常量STRL的定義(一處)即可,以防止大范圍地在每一個(gè)出現(xiàn)STRL的地方修改程序代碼(大范圍地修改代碼很難保證修改的一致性與正確性)。

(3)通常是用指針讀而不是寫(xiě)。例如:

constintSTRL=40;

charbuf[STRL*10+STRL];

char*conststr1=&buf;//定義str1為一常量字符

指針

char*conststr2=&buf[STRL+1]; //定義str2為一常

量字符指針

}

voidf()

{

g(“Thisisatest”);//用一個(gè)常量字符串對(duì)constchar*p賦初值

}

(2)指針常量的定義方式為

指針類型T*const常量名=初值;

注意:

①常量若以函數(shù)的形參方式出現(xiàn),則沒(méi)有初值部分,對(duì)應(yīng)的實(shí)參即為常量的初值。②當(dāng)常量的類型是一個(gè)T*時(shí),即為指針常量,不允許修改的是指針常量本身的值,而不是指針常量所指對(duì)象的值;若指針?biāo)傅膶?duì)象為const,即為指向常量的指針,則不允許修改的是*常量名或常量名[下標(biāo)]對(duì)應(yīng)的值。例如:

//cpc是一個(gè)指向一個(gè)常量字符串的常量指針

constchar*constcpc=s;

*cpc=‘g’; //錯(cuò)誤!?試圖修改cpc所指的內(nèi)容

cpc=p; //錯(cuò)誤!?試圖修改cpc的內(nèi)容

}

由于談到指針時(shí)總涉及兩個(gè)對(duì)象——指針變量本身與指針?biāo)傅膶?duì)象,而C++又允許對(duì)這二者都可進(jìn)行const約束,因此,是指針常量(不允許修改指針本身的值),還是指向常量的指針(指針?biāo)傅膶?duì)象不允許修改)就難以辨別。如何有效地辨別、定義這兩種類型的量呢?BjarneStrostrup給出了一個(gè)有效的方法,即當(dāng)從右至左讀常量定義式時(shí),即可解析其類別。例如:

語(yǔ)句:char*constcp=s;從右至左可讀成cp是一個(gè)指向char類型(變量)的const指針(*讀為指針pointer)。

語(yǔ)句:constchar*pc=s;

從右至左可讀成pc是一個(gè)指向常量字符串的指針變量。

語(yǔ)句:constchar*constcpc=s;

從右至左可讀成cpc是一個(gè)指向常量字符串的常量指針。

C++語(yǔ)法規(guī)定:可將一個(gè)變量的地址賦給一個(gè)常量指針,因?yàn)檫@樣做不會(huì)造成任何傷害;但不允許將一個(gè)常量的地址賦給一個(gè)未加限制的指針(即指針變量),以防止用戶不經(jīng)意/試圖通過(guò)該指針修改常量的值。例如:

5.7引用

引用即為一個(gè)變量的別名。引用的主要用途是向函數(shù)直接傳遞變量(C/C++是值傳遞,稱為CallbyValue)或從函數(shù)返回變量。特別是在實(shí)現(xiàn)大型對(duì)象的傳遞與操作符的重載(OperatorOverloading,詳見(jiàn)第二部分的內(nèi)容)時(shí),引用更是扮演著不可或缺的角色。

引用用符號(hào)X&表示,它表示對(duì)某個(gè)X類型變量的引用。

為了保證引用確實(shí)是對(duì)某個(gè)量的引用(即用引用名綁定所引用的對(duì)象),在定義引用時(shí)一定要對(duì)其進(jìn)行初始化。由于一個(gè)引用所表示的是一個(gè)對(duì)象的別名,所以它不能獨(dú)立存在,必然要關(guān)聯(lián)于某個(gè)已定義的名字,它們對(duì)應(yīng)于同一個(gè)對(duì)象。

關(guān)聯(lián)的方式是:①在定義該引用時(shí)所聲明的初始化值;②對(duì)于被定義為函數(shù)的形參(或返回值)的引用,由實(shí)參(或返回值表達(dá)式)給出對(duì)應(yīng)值。例如:

voidg()

{

intii=0;

int&rr=ii; //rr為ii的引用,rr即為ii的別名

rr++; //等價(jià)于ii++;

int*pp=&rr; //pp==ⅈ*pp==ii;

}

形象地說(shuō),一個(gè)引用好比總是用*(取內(nèi)容)操作的一個(gè)指針常量。

應(yīng)用中,我們有時(shí)可能需要向函數(shù)傳遞/從函數(shù)返回某個(gè)對(duì)象(稱為CallbyReference)而非對(duì)象的值,這時(shí)可采用引用類型。因此,當(dāng)參數(shù)類型為引用類型時(shí),函數(shù)體內(nèi)對(duì)形參的修改實(shí)際上就是對(duì)實(shí)參的修改,這類似于Pascal函數(shù)或過(guò)程用var聲明的形參。例如:

voidswap(int&a,int&b)//在函數(shù)內(nèi)部交換a,b

{

inttemp;

temp=a;

a=b;

b=temp;

}當(dāng)然,我們亦可用指針的方式完成同樣的功能。代碼如下:

voidswap(int*a,int*b)

{

int*temp=0;

*temp=*a;

*a=*b;

*b=*temp;

}引用最主要的應(yīng)用場(chǎng)合是:①傳遞/返回大型對(duì)象;②實(shí)現(xiàn)操作符的重載(本書(shū)第二部分將會(huì)詳細(xì)講述)。其它情況下一般不宜采用引用類型。當(dāng)需要向函數(shù)傳遞/從函數(shù)返回某個(gè)對(duì)象而非對(duì)象的值時(shí),采用指針更易于理解。例如:

//使用引用,不易理解的方式

voidinc(int&aa)

{aa++;} //aa自增1

voidf()

{

intx=1;

inc(x);

//aa、x對(duì)應(yīng)同一對(duì)象,用戶可能不清楚x已改變其值

}

//使用指針,易于理解的方式

voidinc(int*p)

{(*p)++;}

voidf()

{

intx=1;

inc(&x);

//用戶非常清楚x已改變其值并等于2

}

當(dāng)一個(gè)函數(shù)的返回類型為引用時(shí),該函數(shù)的調(diào)用結(jié)果既可作為右值(原意為賦值表達(dá)式的右部,即代表一個(gè)值),亦可作為一個(gè)左值(原意為賦值表達(dá)式的左部,此處代表一個(gè)對(duì)象的存儲(chǔ)空間)。引用的基本用法及應(yīng)用示例如下所示。

例3

用戶從鍵盤(pán)上輸入若干個(gè)token,每個(gè)token用空格相隔,統(tǒng)計(jì)輸出用戶輸入的token及相應(yīng)的次數(shù)。當(dāng)用戶輸入“stringstudentstringasbcuiasas”時(shí),程序運(yùn)行輸出結(jié)果為

string:2

student:1

as:3

bc:1

ui:1

5.8結(jié)構(gòu)

一個(gè)結(jié)構(gòu)是其元素類型為任意類型的若干個(gè)元素的集合,即一個(gè)元素類型可以各不相同的數(shù)組,我們常稱其為異質(zhì)(Heterogeneous)數(shù)組。

相比之下,我們稱數(shù)組為一個(gè)同質(zhì)(Homogeneous)元素的集合。

構(gòu)成一個(gè)結(jié)構(gòu)類型的那些元素稱為它的成員(Member)。

結(jié)構(gòu)類型用來(lái)定義具有多種不同類型(當(dāng)然亦可具有相同的類型)的屬性(Attribute)的客觀事物,而這樣的事物在現(xiàn)實(shí)中是大量存在的。從上例可看出,在結(jié)構(gòu)類型中,每個(gè)成員都有相應(yīng)的類型,各成員的類型可相同亦可不相同。

一個(gè)結(jié)構(gòu)類型看上去只是定義了一種事物的一個(gè)實(shí)例的各個(gè)屬性(用成員表示),但根據(jù)各成員類型的值集可以直接構(gòu)造出這個(gè)結(jié)構(gòu)類型的值集,即可根據(jù)定義的這種結(jié)構(gòu)類型,定義出(實(shí)例化出)具有這種結(jié)構(gòu)的和具有該結(jié)構(gòu)類型值集元素個(gè)數(shù)的同類事物的實(shí)例,這是一種典型的共性抽象。

例如,在上面address結(jié)構(gòu)類型的定義中,實(shí)際上定義了由如下集合構(gòu)造的可具有

個(gè)值的結(jié)構(gòu)類型變量。

用結(jié)構(gòu)類型可以構(gòu)造對(duì)應(yīng)的數(shù)組類型(結(jié)構(gòu)數(shù)組,即數(shù)組的元素為結(jié)構(gòu))、指針類型(指向結(jié)構(gòu)的指針)、引用類型(結(jié)構(gòu)的引用)和結(jié)構(gòu)類型(結(jié)構(gòu)嵌套結(jié)構(gòu))。

結(jié)構(gòu)定義中,其成員類型不能是它自身,否則會(huì)使得編譯器無(wú)法推斷該類型量的存儲(chǔ)分配量而無(wú)法進(jìn)行內(nèi)存分配。例如,我們不能進(jìn)行如下定義:

structNo_good{

No_goodmember;

};

/*存在無(wú)終止條件的遞歸,無(wú)法確定對(duì)應(yīng)的存儲(chǔ)空間的大小*/

而可以進(jìn)行如下的定義:

structLink{

Link*previous;

Link*successor;

};

/*指針類型變量的存儲(chǔ)空間大小不依賴于對(duì)應(yīng)類型*/

C++為任意結(jié)構(gòu)類型提供了一個(gè)對(duì)象的整體賦值操作(二進(jìn)制復(fù)制),但不提供與結(jié)構(gòu)類型變量相關(guān)的其它整體操作(例如比較操作),這些操作可由用戶自己定義(在本書(shū)第二部分會(huì)看到,這些操作所使用的操作符可以與基本類型相同)。例如,對(duì)結(jié)構(gòu)類型address變量可進(jìn)行如下的整體賦值操作:

addresscurrent;

addressset_current(addressnext)

{

staticaddressprev=current; //結(jié)構(gòu)變量的整體賦值

current=next; //結(jié)構(gòu)變量的整體賦值

returnprev;

}

C++提供了取結(jié)構(gòu)類型指定成員值的兩個(gè)操作:

(1)取結(jié)構(gòu)成員操作符·?。

語(yǔ)法:結(jié)構(gòu)變量名·成員名

語(yǔ)義:返回第一操作數(shù)中成員名字與第二操作數(shù)相同的成員的值。

(2)用指針取結(jié)構(gòu)成員操作符->。

語(yǔ)法:指向結(jié)構(gòu)變量的指針變量->成員名

語(yǔ)義:返回第一操作數(shù)所指向的那個(gè)對(duì)象中成員名字與第二操作數(shù)相同的成員的值。

定義結(jié)構(gòu)類型變量時(shí)可進(jìn)行初始化,其方式與數(shù)組類似(用初始化表的方式)。例如:

structstudent //結(jié)構(gòu)類型student的定義

{

stringname;

unsignedintid;

chargender;

}s1={“LiXiaohong”,0304214,‘F’};

//結(jié)構(gòu)類型與結(jié)構(gòu)變量s1同時(shí)定義,并對(duì)s1進(jìn)行初始化

C++為結(jié)構(gòu)類型的對(duì)象分配一塊連續(xù)的存儲(chǔ)空間,并按該結(jié)構(gòu)類型各成員的聲明順序和對(duì)應(yīng)類型對(duì)該空間進(jìn)行劃分,但在分配和劃分時(shí)遵循“字對(duì)準(zhǔn)”原則(即每個(gè)成員的起始地址是一個(gè)字,字的大小與平臺(tái)相關(guān),常見(jiàn)的是2字節(jié)或4字節(jié)),因此,對(duì)一個(gè)結(jié)構(gòu)類型量所實(shí)際分配的空間存儲(chǔ)量可能大于各成員類型對(duì)應(yīng)空間之和。例如,對(duì)以下的address變量jd而言,其內(nèi)存分配示意圖如圖5.8所示。

addressjd={

“JimDandy”, //的初始值

61,

//jd.number的初始值

“SouthSt”,

//jd.street的初始值

“NewProvidence”, //jd.town的初始值

{‘N’,‘J’}, //jd.state的初始值

7974

//jd.zip的初始值

};圖5.8結(jié)構(gòu)類型address變量所分配的存儲(chǔ)空間假定在某一平臺(tái)上,sizeof(char)=1,sizeof(longint)=8,sizeof(char*)=4,根據(jù)adrress的定義,jd的實(shí)際內(nèi)存分配量為

sizeof(jd)=sizeof(char*)+sizeof(longint)+sizeof(char*)

+sizeof(char*)+2*sizeof(char)+2+sizeof(long)

=32字節(jié)

而非成員實(shí)際應(yīng)有的內(nèi)存量30字節(jié)。注意上式中的+2

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 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ì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論