C-C指針指針應(yīng)用詳解_第1頁
C-C指針指針應(yīng)用詳解_第2頁
C-C指針指針應(yīng)用詳解_第3頁
C-C指針指針應(yīng)用詳解_第4頁
C-C指針指針應(yīng)用詳解_第5頁
已閱讀5頁,還剩32頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

c_c++指針指針應(yīng)用詳解

前言:復(fù)雜類型說明

要了解指針,多多少少會出現(xiàn)一些比較復(fù)雜的類型,所以我先介紹

一下如何完全理解一個復(fù)雜類型,要理解復(fù)雜類型其實(shí)很簡單,一個類

型里會出現(xiàn)很多運(yùn)算符,他們也像普通的表達(dá)式一樣,有優(yōu)先級,其優(yōu)

先級和運(yùn)算優(yōu)先級一樣,所以我總結(jié)了一下其原則:

從變量名處起,根據(jù)運(yùn)算符優(yōu)先級結(jié)合,一步一步分析.

下面讓我們先從簡單的類型開始慢慢分析吧:

intp;〃這是一個普通的整型變量

int*p;〃首先從P處開始,先與*結(jié)合,所以說明P是一個指針,然后

再與int結(jié)合,說明指針?biāo)赶虻膬?nèi)容的類型為int型.所以P是一個返

回整型數(shù)據(jù)的指針

intp網(wǎng);〃首先從P處開始,先與口結(jié)合,說明P是一個數(shù)組,然后與

int結(jié)合,說明數(shù)組里的元素是整型的,所以P是一個由整型數(shù)據(jù)組成的

數(shù)組

int*p⑶;〃首先從P處開始冼與口結(jié)合,因?yàn)槠鋬?yōu)先級比*高,所以P

是一個數(shù)組,然后再與*結(jié)合,說明數(shù)組里的元素是指針類型,然后再與

int結(jié)合,說明指針?biāo)赶虻膬?nèi)容的類型是整型的,所以P是一個由指向

整型數(shù)據(jù)的指針?biāo)M成的數(shù)組.

int(*p)[3];〃首先從P處開始,先與*結(jié)合,說明P是一個指針

〃然后再與口結(jié)合(與"()"這步可以忽略,只是為

〃了改變優(yōu)先級),說明指針?biāo)赶虻膬?nèi)容是一個

〃數(shù)組,然后再與int結(jié)合,說明數(shù)組里的元素是

〃整型的.所以P是一個指向由整型數(shù)據(jù)組成的數(shù)

〃組的指針.

int**p;〃首先從P開始,先與*結(jié)合,說是P是一個指針,然

〃后再與*結(jié)合,說明指針?biāo)赶虻脑厥侵羔?,?/p>

〃后再與int結(jié)合,說明該被指向的指針?biāo)赶虻脑厥钦?/p>

〃型數(shù)據(jù).由于二級指針以及更高級的指針極少用

〃在復(fù)雜的類型中,所以后面更復(fù)雜的類型我們就

〃不考慮多級指針了,最多只考慮一級指針.

intp(int);〃從P處起,先與()結(jié)合,說明P是一個函數(shù),然后進(jìn)入

〃()里分析,說明該函數(shù)有一個整型變量的參數(shù)

〃然后再與外面的int結(jié)合,說明函數(shù)的返回值是

〃一個整型數(shù)據(jù)

int(*p)(int);〃從P處開始,先與指針結(jié)合,說明P是一個指針燃后

〃()結(jié)合,說明指針指向的是一個函數(shù),然后再與()里的

//int結(jié)合,說明函數(shù)有一個int型的參數(shù),再與最外層的

//int結(jié)合,說明函數(shù)的返回類型是整型,所以P是一個指

〃向有一個整型參數(shù)且返回類型為整型的函數(shù)的指針.

lnt(*a[10])(int);〃一個有10個指針的數(shù)組,該指針指向一個函數(shù),

該函數(shù)有一個整型參數(shù)并返回一個整型數(shù)

(Anarrayoftenpointerstofunctionsthattakeanintegerargumentandreturna

ninteger).

int*(*p(int))[3];〃可以先跳過,不看這個類型,過于復(fù)雜

〃從P開始,先與()結(jié)合,說明P是一個函數(shù),然后進(jìn)

〃入()里面,與int結(jié)合,說明函數(shù)有一個整型變量

〃參數(shù),然后再與外面的*結(jié)合,說明函數(shù)返回的是

〃一個指針,然后到最外面一層,先與口結(jié)合,說明

〃返回的指針指向的是一個數(shù)組,然后再與*結(jié)合,說

〃明數(shù)組里的元素是指針,然后再與int結(jié)合,說明指

〃針指向的內(nèi)容是整型數(shù)據(jù).所以P是一個參數(shù)為一個

〃整數(shù)據(jù)且返回一個指向由整型指針變量組成的數(shù)組

〃的指針變量的函數(shù).

說到這里也就差不多了,我們的任務(wù)也就這么多,理解了這兒個類

型,其它的類型對我們來說也是小菜了,不過我們一般不會用太復(fù)雜的

類型,那樣會大大減小程序的可讀性,請慎用,這上面的兒種類型已經(jīng)

足夠我們用了.

⑴、細(xì)說指針

指針是一個特殊的變量,它里面存儲的數(shù)值被解釋成為內(nèi)存里的

一個地址。要搞清一個指針需要搞清指針的四方面的內(nèi)容:指針的類

型、指針?biāo)赶虻念愋汀⒅羔樀闹祷蛘呓兄羔標(biāo)赶虻膬?nèi)存區(qū)、指針

本身所占據(jù)的內(nèi)存區(qū)。讓我們分別說明。

先聲明幾個指針放著做例子:

例一:

(l)int*ptr;

(2)char*ptr;

(3)int**ptr;

(4)int(*ptr)[3];

(5)int*(*ptr)[4];

a.指針的類型

從語法的角度看,你只要把指針聲明語句里的指針名字去掉,剩

下的部

分就是這個指針的類型。這是指針本身所具有的類型。讓我們看

看例一中各個指針的類型:

(l)int*ptr;〃指針的類型是int*

(2)char*ptr;〃指針的類型是char*

⑶int**ptr;〃指針的類型是int**

⑷int(*ptr)⑶;〃指針的類型是int(*)[3]

⑸int*(*ptr)⑷;〃指針的類型是int*(*)[4]

怎么樣?找出指針的類型的方法是不是很簡單?

b.指針?biāo)赶虻念愋?/p>

當(dāng)你通過指針來訪問指針?biāo)赶虻膬?nèi)存區(qū)時(shí)、指針?biāo)赶虻念愋?/p>

決定了編譯器將把那片內(nèi)存區(qū)里的內(nèi)容當(dāng)做什么來看待。

從語法上看,你只須把指針聲明語句中的指針名字和名字左邊的

指針聲明符*去掉,剩下的就是指針?biāo)赶虻念愋汀@?

(l)int*ptr;〃指針?biāo)赶虻念愋褪莍nt

(2)char*ptr;〃指針?biāo)赶虻牡念愋褪莄har

(3)int**ptr;〃指針?biāo)赶虻牡念愋褪莍nt*

⑷int(*ptr)⑶;〃指針?biāo)赶虻牡念愋褪莍nt()⑶

⑸int*(*ptr)⑷;〃指針?biāo)赶虻牡念愋褪莍nt*()[4]

在指針的算術(shù)運(yùn)算中,指針?biāo)赶虻念愋陀泻艽蟮淖饔谩?/p>

指針的類型(即指針本身的類型)和指針?biāo)赶虻念愋褪莾蓚€概念。

當(dāng)你

對C越來越熟悉時(shí),你會發(fā)現(xiàn),把與指針攪和在一起的"類型"這

個概念分成"指針的類型"和"指針?biāo)赶虻念愋?兩個概念,是精通指

針的關(guān)鍵點(diǎn)之一。

我看了不少書,發(fā)現(xiàn)有些寫得差的書中,就把指針的這兩個概念

攪在一起了,

所以看起書來前后矛盾,越看越糊涂。

c.指針的值--或者叫指針?biāo)赶虻膬?nèi)存區(qū)的地址

指針的值是指針本身存儲的數(shù)值,這個值將被編譯器當(dāng)作一個地

址,而

不是一個一般的數(shù)值。在32位程序里,所有類型的指針的值都

是一個32位

整數(shù),因?yàn)?2位程序里內(nèi)存地址全都是32位長。指針?biāo)赶虻?/p>

內(nèi)存區(qū)就

是從指針的值所代表的那個內(nèi)存地址開始,長度為sizeof(指針?biāo)?/p>

指向的類

型)的一片內(nèi)存區(qū)。以后,我們說一個指針的值是XX,就相當(dāng)于

說該指針指

向了以XX為首地址的一片內(nèi)存區(qū)域;我們說一個指針指向了某

塊內(nèi)存區(qū)域,就相當(dāng)于說該指針的值是這塊內(nèi)存區(qū)域的首地址。

指針?biāo)赶虻膬?nèi)存區(qū)和指針?biāo)赶虻念愋褪莾蓚€完全不同的概

念。在例

一中,指針?biāo)赶虻念愋鸵呀?jīng)有了,但由于指針還未初始化,所

以它所指向

的內(nèi)存區(qū)是不存在的,或者說是無意義的。

以后,每遇到一個指針,都應(yīng)該問問:這個指針的類型是什么?

指針指向

的類型是什么?該指針指向了內(nèi)存II中的哪里?(重點(diǎn)注意)

d.指針本身所占據(jù)的內(nèi)存區(qū)

指針本身占了多大的內(nèi)存?你只要用函數(shù)sizeof(指針的類型)測

一下

就知道了。在32位平臺里,指針本身占據(jù)了4個字節(jié)的長度。

指針本身占據(jù)的內(nèi)存這個概念在判斷一個指針表達(dá)式(后面會解

釋)是

否是左值時(shí)很有用。

⑵、指針的算術(shù)運(yùn)算

指針可以加上或減去一個整數(shù)。指針的這種運(yùn)算的意義和通常的

數(shù)值的加減

運(yùn)算的意義是不一樣的,以單元為單位。例如:

例二:

chara[20];

int*ptr=(int*)a;〃強(qiáng)制類型轉(zhuǎn)換并不會改變a的類型,只改變ptr由

它的值開始的所指向的〃內(nèi)存區(qū)的的長度(sizeof(int)).

ptr++;

在上例中,指針ptr的類型是int*,它指向的類型是int,它被初始

為指向整型變量a。接下來的第3句中,指針ptr被加了1,編

譯器是這樣

處理的:它把指針ptr的值加上了l*sizeof(int),在32位程序中,

是被加上

了4,因?yàn)樵?2位程序中,int占4個字節(jié)。由于地址是用字節(jié)

做單位的,

故ptr所指向的地址由原來的變量a的地址向高地址方向增加了

4個字節(jié)。

由于char類型的長度是一個字節(jié),所以,原來ptr是指向數(shù)組a

的第0號

單元開始的四個字節(jié),此時(shí)指向了數(shù)組a中從第4號單元開始的

四個字節(jié)。

我們可以用一個指針和一個循環(huán)來遍歷一個數(shù)組,看例子:

例三:

intarray[20]={0};

int*ptr=array;

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

(

(*ptr)++;〃指針?biāo)赶虻脑氐闹?1

ptr++;〃指針指向下一個元素

}

這個例子將整型數(shù)組中各個單元的值加lo由于每次循環(huán)都將指

針ptr加1個單元,所以每次循環(huán)都能訪問數(shù)組的下一個單元。

再看例子:

例四:

chara[20]="You_are_a_girl";

int*ptr=(int*)a;

ptr+=5;

在這個例子中,ptr被加上了5,編譯器是這樣處理的:將指針

ptr的值加上5乘sizeof(int),在32位程序中就是加上了5乘4=20。

由于地址的單位是字節(jié),故現(xiàn)在的ptr所指向的地址比起加5后的ptr

所指向的地址來說,向高地址方向移動了20個字節(jié)。在這個例子中,

沒加5前的ptr指向數(shù)組a的第。號單元開始的四個字節(jié),加5后,

ptr已經(jīng)指向了數(shù)組a的合法范圍之外了。雖然這種情況在應(yīng)用上會

出問題,但在語法上卻是可以的。這也體現(xiàn)出了指針的靈活性。

如果上例中,ptr是被減去5,那么處理過程大同小異,只不過

ptr的值是被減去5乘sizeof(int),新的ptr指向的地址將比原來的ptr

所指向的地址向低地址方向移動了20個字節(jié)。

下面請?jiān)试S我再舉一個例子:(一個誤區(qū))

例五:

#include<stdio.h>

intmain()

(

chara[20]="You_are_a_girl";

char*p=a;

char**ptr=&p;

//printf("p=%d\n",p);

//printf("ptr=%d\n",ptr);

//printf("*ptr=%d\n",*ptr);

printf("**ptr=%c\n",**ptr);

ptr++;

//printf("ptr=%d\n",ptr);

//printf("*ptr=%d\n"/*ptr);

printf("**ptr=%c\n",**ptr);

}

誤區(qū)一、輸出答案為Y和。

誤解:ptr是一個char的二級指針,當(dāng)執(zhí)行ptr++;時(shí),會使指針加一

sizeof(char),所以輸出如上結(jié)果,這個可能只是少部分人的結(jié)果.

誤區(qū)二、輸出答案為Y和a

誤解:ptr指向的是一個char*類型,當(dāng)執(zhí)行ptr++;時(shí),會使指針加一

sizeof(char*)(有可能會有人認(rèn)為這個值為1,那就會得到誤區(qū)一的

案,這個值應(yīng)該是4,參考前面內(nèi)容),即&p+4哪進(jìn)行一次取值

運(yùn)算不就指向數(shù)組中的第五個元素了嗎?那輸出的結(jié)果不就是數(shù)組中

第五個元素了嗎?答案是否定的.

正解:ptr的類型是char**,指向的類型是一個char*類型,該指向的

地址就是p的地址(&p),當(dāng)執(zhí)行ptr++;時(shí),會使指針加一個

sizeof(char

*),即&p+4;那*(&p+4)指向哪呢,這個你去問上帝吧,或者

他會告訴你在哪?所以最后的輸出會是一個隨機(jī)的值,或許是一個非

法操作.

總結(jié)一下:

一個指針ptrold力口(減)一個整數(shù)n后,結(jié)果是一個新的指針

ptrnew,ptrnew的類型和ptrold的類型相同,ptrnew所指向的類型

和ptrold所指向的類型也相同。ptrnew的值將比ptrold的值增加(減

少)了n乘sizeof(ptrold所指向的類型)個字節(jié)。就是說,ptrnew所指

向的內(nèi)存

區(qū)將比ptrold所指向的內(nèi)存區(qū)向高(低)地址方向移動了n乘

sizeof(ptrold所指向的類型)個字節(jié)。

指針和指針相減:

兩個指針不能進(jìn)行加法運(yùn)算,這是非法操作,因?yàn)檫M(jìn)行加法后,

得到的結(jié)果指向一個不知所向的地方,而且毫無意義。兩個指針可以

進(jìn)行減法操作,但必須類型相同,一般用在數(shù)組方面,不多說了。

⑶、運(yùn)算符&和*

這里&是取地址運(yùn)算符,*是間接運(yùn)算符。

&a的運(yùn)算結(jié)果是一個指針,指針的類型是a的類型加個*,

指針?biāo)?/p>

指向的類型是a的類型,指針?biāo)赶虻牡刂罚ㄖ羔樀闹担┞铮蔷?/p>

是a的地址。*p的運(yùn)算結(jié)果就五花八門了。總之*p的結(jié)果是p所指

向的東西,這個東西有這些特點(diǎn):它的類型是p指向的類型,它所占

用的地址是p所指向的地址。

例六:

inta=12;intb;int*p;int**ptr;

p=&a;〃&a的結(jié)果是一個指針,類型是int*,指向的類

型是

〃int,指向的地址是a的地址。

*p=24;〃*p的結(jié)果,在這里它的類型是int,它所占用的地址是

〃p所指向的地址,顯然,*P就是P所指向的東西即變量a。

ptr=&p;//&p的結(jié)果是個指針,該指針的類型是p的類

型加個*,

〃在這里是int**。該指針?biāo)赶虻念愋褪莗的類型,這

〃里是int*。該指針?biāo)赶虻牡刂肪褪侵羔榩自己的地址。

*ptr=&b;〃*ptr是個指針,&b的結(jié)果也是個指針,且這

兩個指針

〃的類型和所指向的類型是一樣的,所以用&b來給*ptr賦

〃值就是毫無問題的了。

**ptr=34;//*ptr的結(jié)果是ptr所指向的東西,在這里是一個指針,

〃對這個指針再做一次*運(yùn)算,結(jié)果是一個對類型的變量。

⑷、指針表達(dá)式

一個表達(dá)式的結(jié)果如果是一個指針,那么這個表達(dá)式就叫指針表

式。下面是一些指針表達(dá)式的例子:

例七:

inta,b;

intarray[10];

int*pa;

pa=&a;//&a是一個指針表達(dá)式。

lnt**ptr=&pa;//&pa也是一個指針表達(dá)式。

*ptr=&b;//*ptr和&b都是指針表達(dá)式。

pa=array;

pa++;〃這也是指針表達(dá)式。

例八:

char*arr[20];

char**parr=arr;〃如果把a(bǔ)rr看作指針的話,arr也是指針表達(dá)式

char*str;

str=*parr;//*parr是指針表達(dá)式

str=*(parr+l);〃*(parr+l)是指針表達(dá)式

str=*(parr+2);〃*(parr+2)是指針表達(dá)式

由于指針表達(dá)式的結(jié)果是一個指針,所以指針表達(dá)式也具有指針

具有的四個要素:指針的類型,指針?biāo)赶虻念愋?,指針指向?/p>

內(nèi)存區(qū),指針自身占據(jù)的內(nèi)存。

好了,當(dāng)一個指針表達(dá)式的結(jié)果指針已經(jīng)明確地具有了指針自身

據(jù)的內(nèi)存的話,這個指針表達(dá)式就是一個左值,否則就不是一個

左值。在例七中,&a不是一個左值,因?yàn)樗€沒有占據(jù)明確的

內(nèi)存。*ptr是一個左值,因?yàn)?ptr這個指針已經(jīng)占據(jù)了內(nèi)存,其實(shí)*ptr

就是指針pa,既然pa已經(jīng)在內(nèi)存中有了自己的位置,那么*ptr當(dāng)然

也有了自己的位置。

⑸、數(shù)組和指針的關(guān)系

數(shù)組的數(shù)組名其實(shí)可以看作一個指針??聪吕?/p>

例九:

intarray[10]={0,l/2,3,4,5,6,7,8/9}/value;

value=array[O];〃也可寫成:value=*array;

value=array[3];〃也可寫成:value=*(array+3);

value=array[4];〃也可寫成:value=*(array+4);

上例中,一般而言數(shù)組名array代表數(shù)組本身,類型是

但如果把a(bǔ)rray看做指針的話,它指向數(shù)組的第0個單元,類型是int*,

所指向的類型是數(shù)組單元的類型即int。因此*array等于。就一點(diǎn)也不

奇怪了。同理,array+3是一個指向數(shù)組第3個單元的指針,所以

*(array+3)等于3。其它依此類推。

例十:

char*str[3]={

"HellO/thisisasample",

"Hi,goodmorning.",

"Helloworld"

};

chars[80];

strcpy(s,str[O]);〃也可寫成strcpy(s/str);

strcpy(s,str[l]);〃也可寫成strcpy(s,*(str+l));

strcpy(s,str[2]);〃也可寫成strcpy(s,*(str+2));

上例中,str是一個三單元的指針數(shù)組,該數(shù)組的每個單元都是

一個指針,這些指針各指向一個字符串。把指針數(shù)組名str當(dāng)作一個

指針的話,它指向數(shù)組的第0號單元,它的類型是char**,它指向的

類型是char*o

*str也是一個指針,它的類型是char*,它所指向的類型是char,

指向的地址是字符串"Hello/thisisasample!"的第一個字符的地址,

'H'的地址。注意:字符串相當(dāng)于是一個數(shù)組,在內(nèi)存中以

數(shù)組的形式儲

存,只不過字符串是一個數(shù)組常量,內(nèi)容不可改變,且只能是右值.

如果

看成指針的話,他即是常量指針,也是指針常量.

str+1也是一個指針,它指向數(shù)組的第1號單元,它的類型是

char**,

它指向的類型是char*o

*(str+l)也是一個指針,它的類型是char*,它所指向的類型是char,

它指向"Hi,goodmorning."的第—個字符'H'.

下面總結(jié)一下數(shù)組的數(shù)組名(數(shù)組中儲存的也是數(shù)組)的問題:

聲明了一個數(shù)組TYPEarray[n],則數(shù)組名稱array就有了兩重含義:

第一,它代表整個數(shù)組,它的類型是TYPE[n];第二,它是一個

常量

指針,該指針的類型是TYPE*,該指針指向的類型是TYPE,也就

是數(shù)組

單元的類型,該指針指向的內(nèi)存區(qū)就是數(shù)組第。號單元,該指針

自己占

有單獨(dú)的內(nèi)存區(qū),注意它和數(shù)組第0號單元占據(jù)的內(nèi)存區(qū)是不同

的。該

指針的值是不能修改的,即類似array++的表達(dá)式是錯誤的。

在不同的表達(dá)式中數(shù)組名array可以扮演不同的角色:

⑴在表達(dá)式sizeof(array)(等價(jià)于sizeof(int[N]))中,數(shù)組名array代

表數(shù)組本身,故這時(shí)sizeof函數(shù)測出的是整個數(shù)組的大小而不是指針

的大小。

⑴在表達(dá)式*array中,array扮演的是指針,因此這個表達(dá)式的結(jié)

果就是

數(shù)組第0號單元的大小。sizeoW*array)測出的是數(shù)組單元的大小。

⑴表達(dá)式array+n(其中n=0,1,2,.....)中,array扮演的是指

針,故array+n的結(jié)果是一個指針,它的類型是TYPE*,它指向

的類型是TYPE,它指向數(shù)組第n號單元。故sizeof(array+n)測出的是

指針類型的大小。在32位程序中結(jié)果是4.例十一:

intarray[10];〃array:指向數(shù)組首個單元的指針(數(shù)組首個單元的地

址)或代表數(shù)組本身.int(*ptij[10];〃ptr:指向整個數(shù)組的指針.

ptr=&array;〃&array:整個數(shù)組的首地址.

上例中ptr是一個指針,它的類型是int(*)[10],他指向的類型是

int[10],我們用整個數(shù)組的首地址來初始化它。在語句

ptr=&array

中,array代表數(shù)組本身。

本節(jié)中提到了運(yùn)算符sizeof(),那么我來問一問,sizeof(指針名稱)

測出的是指針自身類型的大小呢還是指針?biāo)赶虻念愋偷拇?/p>

???

答案是前者。例如:

int(*ptr)[10];

則在32位程序中,有:

sizeof(int(*)[10])==4

sizeof(int[10])==40

sizeof(ptr)==4

實(shí)際上,sizeof(對象)測出的都是對象自身的類型的大小,而不是

別的

什么類型的大小。

⑹、指針和結(jié)構(gòu)類型的關(guān)系

可以聲明一個指向結(jié)構(gòu)類型對象的指針。

例十二:

structMyStruct

(

inta;

intb;

intc;

);

structMyStructss={20/30/40};

〃聲明了結(jié)構(gòu)對象ss,并把ss的成員初始化為20,30和40。

structMyStruct*ptr=&ss;

〃聲明了一個指向結(jié)構(gòu)對象SS的指針。它的類型是

〃MyStruct*,它指向的類型是MyStructo

int*pstr=(int*)&ss;

〃聲明了一個指向結(jié)構(gòu)對象ss的指針。但是pstr和

〃ptr所指向的類型是不同的。

請問怎樣通過指針ptr來訪問ss的三個成員變量?

答案:

ptr->a;〃指向運(yùn)算符,或者可以這們(*ptr).a,建議使用前者

ptr->b;

ptr->c;

又請問怎樣通過指針pstr來訪問ss的三個成員變量?

答案:

*pstr;〃訪問了ss的成員a。

*(pstr+l);〃訪問了ss的成員bo

*(pstr+2)〃訪問了ss的成員Co

雖然我在我的MSVC++6.0上調(diào)式過上述代碼,但是要知道,這

樣使用pstr來訪問結(jié)構(gòu)成員是不正規(guī)的,為了說明為什么不正規(guī),讓

我們看看怎樣通過指針來訪問數(shù)組的各個單元乂將結(jié)構(gòu)體換成數(shù)組)

例十三:

intarray[3]={35,56,37};

int*pa=array;

通過指針pa訪問數(shù)組array的三個單元的方法是:

*pa;〃訪問了第0號單元

*(pa+l);〃訪問了第1號單元

*(pa+2);〃訪問了第2號單元

從格式上看倒是與通過指針訪問結(jié)構(gòu)成員的不正規(guī)方法的格式

一樣。

所有的C/C++編譯器在排列數(shù)組的單元時(shí),總是把各個數(shù)組單元

放在連續(xù)的存儲區(qū)里,單元和單元之間沒有空隙。但在存放結(jié)構(gòu)

對象的各個成員時(shí)一,在某種編譯環(huán)境下,可能會需要字對齊或雙字對

齊或者是別的什么對齊,需要在相鄰兩個成員之間加若干個"填充字

節(jié)",這就導(dǎo)致各個成員之間可能會有若干個字節(jié)的空隙。

所以,在例十二中,即使*pstr訪問到了結(jié)構(gòu)對象ss的第一個成

員變量a,也不能保證*(pstr+l)就一定能訪問到結(jié)構(gòu)成員b。因

為成員a和成員b之間可能會有若干填充字節(jié),說不定*(pstr+l)就正

好訪問

到了這些填充字節(jié)呢。這也證明了指針的靈活性。要是你的目的

就是想看看各個結(jié)構(gòu)成員之間到底有沒有填充字節(jié),嘿,這倒是個不

錯的方法。不過指針訪問結(jié)構(gòu)成員的正確方法應(yīng)該是象例十二中使用

指針ptr的方法。

[7]、指針和函數(shù)的關(guān)系

可以把一個指針聲明成為一個指向函數(shù)的指針。

intfunl(char*,int);

int(*pfunl)(char*/int);

pfunl=funl;

inta=(*pfunl)("abcdefg",7);〃通過函數(shù)指針調(diào)用函數(shù)。

可以把指針作為函數(shù)的形參。在函數(shù)調(diào)用語句中,可以用指針表

達(dá)式來作為實(shí)參。

例十四:

intfun(char*);

inta;

charstr[]="abcdefghijklmn";

a=fun(str);

intfun(char*s)

(

intnum=O;

for(inti=0;;)

(

num+=*s;s++;

)

returnnum;

}

這個例子中的函數(shù)fun統(tǒng)計(jì)一個字符串中各個字符的ASCII碼值

之和。前面說了,數(shù)組的名字也是一個指針。在函數(shù)調(diào)用中,當(dāng)把str

作為實(shí)參傳遞給形參s后,實(shí)際是把str的值傳遞給了s,s所指向的

地址就和str所指向的地址一致,但是str和s各自占用各自的存儲空

間。在函數(shù)體內(nèi)對s進(jìn)行自加1運(yùn)算,并不意味著同時(shí)對str進(jìn)行了

自加1運(yùn)算。

網(wǎng)、指針類型轉(zhuǎn)換

當(dāng)我們初始化一個指針或給一個指針賦值時(shí)一,賦值號的左邊是一

個指針,賦值號的右邊是一個指針表達(dá)式。在我們前面所舉的例子中,

絕大多數(shù)情況下,指針的類型和指針表達(dá)式的類型是一樣的,指針?biāo)?/p>

指向的類型和指針表達(dá)式所指向的類型是一樣的。

例十五:

floatf=12.3;

float*fptr=&f;

int*p;

在上面的例子中,假如我們想讓指針p指向?qū)崝?shù)f,應(yīng)該怎么辦?

是用下面的語句嗎?

p=&f;

不對。因?yàn)橹羔榩的類型是int*,它指向的類型是int。表達(dá)式

&f的結(jié)果是一個指針,指針的類型是float*,它指向的類型

是floato

兩者不一致,直接賦值的方法是不行的。至少在我的MSVC++6.0

上,對指針的賦值語句要求賦值號兩邊的類型一致,所指向的類型也

一致,其它的編譯器上我沒試過,大家可以試試。為了實(shí)現(xiàn)我們的目

的,需要進(jìn)行"強(qiáng)制類型轉(zhuǎn)換":

p=(int*)&f;

如果有一個指針p,我們需要把它的類型和所指向的類型改為

TYEP*TYPE,那么語法格式是:(TYPE*)p;

這樣強(qiáng)制類型轉(zhuǎn)換的結(jié)果是一個新指針,該新指針的類型是

TYPE*,它指向的類型是TYPE,它指向的地址就是原指針指向的

地址。而原來的指針p的一切屬性都沒有被修改。(切記)

一個函數(shù)如果使用了指針作為形參,那么在函數(shù)調(diào)用語句的實(shí)參

和形參的結(jié)合過程中,必須保證類型一致,否則需要強(qiáng)制轉(zhuǎn)換.

例十六:

voidfun(char*);

inta=125,b;

fun((char*)&a);

voidfun(char*s)

(

chare;

c=*(s+3);*(s+3)=*(s+0);*(s+0)=c;

c=*(s+2);*(s+2)=*(s+l);*(s+l)=c;

}

注意這是一個32位程序,故int類型占了四個字節(jié),char類型占

一個字節(jié)。函數(shù)fun的作用是把一個整數(shù)的四個字節(jié)的順序來個顛倒。

注意到了嗎?在函數(shù)調(diào)用語句中,實(shí)參&a的結(jié)果是一個指針,

它的類型是int*,它指向的類型是int。形參這個指針的類型是char*,

它指向的類型是char。這樣,在實(shí)參和形參的結(jié)合過程中,我們必須

進(jìn)行一次從int*類型到char*類型的轉(zhuǎn)換。結(jié)合這個例子,我們可以

這樣來想象編譯器進(jìn)行轉(zhuǎn)換的過程:編譯器先構(gòu)造一個臨時(shí)指針

char*temp,然后執(zhí)行temp=(char*)&a,最后再把temp的值傳遞

給s。所以最后的結(jié)果是:s的類型是char*,它指向的類型是char,它

指向的地址就是a的首地址。

我們已經(jīng)知道,指針的值就是指針指向的地址,在32位程序中,

指針的值其實(shí)是一個32位整數(shù)。那可不可以把一個整數(shù)當(dāng)作指

針的值直接賦給指針呢?就象下面的語句:

unsignedinta;

TYPE*ptr;//TYPE是int,char或結(jié)構(gòu)類型等等類型。

a=20345686;〃無符號整數(shù)a的值用來表示一個地址.

ptr=20345686;〃我們的目的是要使指針ptr指向地址20345686

ptr=a;〃我們的目的是要使指針ptr指向地址20345686

編譯一下吧。結(jié)果發(fā)現(xiàn)后面兩條語句全是錯的。那么我們的目的

就不能

達(dá)到了嗎?不,還有辦法:

unsignedinta;

TYPE*ptr;//TYPE是int,char或結(jié)構(gòu)類型等等類型。

a=N//N必須代表一個合法的地址;

ptr=(TYPE*)a;〃呵呵,這就可以了。

嚴(yán)格說來這里的(TYPE*)和指針類型轉(zhuǎn)換中的(TYPE*)還不一樣。

這里的(TYPE*)的意思是把無符號整數(shù)a的值當(dāng)作一個地址來看待。上

面強(qiáng)調(diào)了a的值必須代表一個合法的地址,否則的話,在你使用ptr

的時(shí)候,就會出現(xiàn)非法操作錯誤。

想想能不能反過來,把指針指向的地址即指針的值當(dāng)作一個整數(shù)

取出來。完全可以。下面的例子演示了把一個指針的值當(dāng)作一個整數(shù)

取出來,然后再把這個整數(shù)當(dāng)作一個地址賦給一個指針:

例十七:

inta=123,b;

int*ptr=&a;//ptr的值是a的地址,即ptr指向a的地址.

char*str;

b=(int)ptr;〃把指針ptr的值當(dāng)作一個整數(shù)取出來賦給整數(shù)bo

str=(char*)b;〃把這個整型值當(dāng)作一個地址賦給char*型指針str。

現(xiàn)在我們已經(jīng)知道了,可以把指針的值當(dāng)作一個整數(shù)取出來,也可以

把一個整數(shù)值當(dāng)作地址賦給一個指針。

⑼、指針的安全問題

看下面的例子:

例十八:

chars='a';

int*ptr;

ptr=(int*)&s;

*ptr=1298;

指針ptr是一個int*類型的指針,它指向的類型是int。它指向

的地址就是s的首地址。在32位程序中,s占一個字節(jié),int類

型占四個字節(jié)。最后一條語句不但改變了s所占的一個字節(jié),還把和

s相臨的高地址方向的三個字節(jié)也改變了。這三個字節(jié)是干什么的?

只有編譯程序知道,而寫程序的人是不太可能知道的。也許這三個字

節(jié)里存儲了非常重要的數(shù)據(jù),也許這三個字節(jié)里正好是程序的一條代

碼,而由于你對指針的馬虎應(yīng)用,這三個字節(jié)的值被改變了!這會造

成崩潰性的錯誤。讓我們再來看一例:

例十九:

chara;

int*ptr=&a;

ptr++;

*ptr=115;

該例子完全可以通過編譯,并能執(zhí)行。但是看到?jīng)]有?第3句對

指針ptr進(jìn)行自加1運(yùn)算后,ptr指向了和整形變量a相鄰的高地址方

向的一塊存儲區(qū)。這塊存儲區(qū)里是什么?我們不知道。有可能它是一

個非常重要的數(shù)據(jù),甚至可能是一條代碼。而第4句竟然往這片存儲

區(qū)里寫

入一個數(shù)據(jù)!這是嚴(yán)重的錯誤。所以在使用指針時(shí),程序員心里

必須非

常清楚:我的指針究竟指向了哪里。在用指針訪問數(shù)組的時(shí)候,

也要注

意不要超出數(shù)組的低端和高端界限,否則也會造成類似的錯誤。

在指針的強(qiáng)制類型轉(zhuǎn)換:ptrl=(TYPE*)ptr2中,如果sizeof(ptr2

的類型)大于sizeof(ptrl的類型),那么在使用指針ptrl來訪問ptr2

所指向的存儲區(qū)時(shí)是安全的。如果sizeof(ptr2的類型)小于

sizeof(ptrl的類型),那么在使用指針ptrl來訪問ptr2所指向的

儲區(qū)時(shí)是不安全的。至于為什么,讀者結(jié)合例十八來想一想,應(yīng)

該會明

白的。

C++是一種靜態(tài)類型的語言,類型安全在C++中舉足輕重.在C語言

中,你可以用void*來指向一切;但在C++中,void*并不能指向一切,

事實(shí)上,在C++中,想找到一個通用的指針,特別是通用的函數(shù)指針

簡直是一個"不可能任務(wù)".就算能,也失去了類型安全的意義了.類型

安全往往能幫我們找出程序中潛在的一些BUG.

1、數(shù)據(jù)指針:

下面我們來探討一下,C++中如何存儲各種類型數(shù)據(jù)的指針.數(shù)據(jù)

指針分為兩種:常規(guī)數(shù)據(jù)指針和成員數(shù)據(jù)指針.

L1[常規(guī)數(shù)據(jù)指針]

這個不用說明了,和C語言一樣,定義、賦值是很簡單明了的.常見

的有:int*,double*等等.如:

intvalue=123;

int*pn=&value;

L2[成員數(shù)據(jù)指針]

有如下的結(jié)構(gòu):

structMyStruct

(

intkey;

intvalue;

};

現(xiàn)在有一個結(jié)構(gòu)對象:

MyStructme;

MyStruct*pMe=&me;

我們需要value成員的地址,我們可以:

int*pValue=&me.value;〃或int*pValue=&pMe->value;

當(dāng)然了,這個指針仍然是屬于第一種范籌--常規(guī)數(shù)據(jù)指針.

好了,我們現(xiàn)在需要一種指針,它指向MyStruct中的任一數(shù)據(jù)成員,

那么它應(yīng)該是這樣的子:

intMyStruct::*pMV=&MyStruct::value;//^

intMyStruct::*pMK=&MyStruct::key;這種指針的用途是用于取得

結(jié)構(gòu)成員在結(jié)構(gòu)內(nèi)的地址.我們可以通過該指針來訪問成員數(shù)據(jù):

intvalue=pMe->*pMV;〃取得pMe的value成員數(shù)據(jù).

intkey=me.*pMK;〃取得me的key成員數(shù)據(jù).

那么,在什么場合下會使用到成員數(shù)據(jù)指針呢?

確實(shí),成員指針本來就不是一種很常用的指針.不過,在某些時(shí)候

還是很有用處的.我們先來看看下面的一個函數(shù):

intsum(MyStruct*objs,intMyStruct::*pm/intcount)

(

intresult=O;

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

result+=objs[i].*pm;

returnresult;

}

這個函數(shù)的功能是什么,你能看明白嗎?它的功能就是,給定count

個MyStruct結(jié)構(gòu)的指針,計(jì)算出給定成員數(shù)據(jù)的總和.

有點(diǎn)拗口對吧?看看下面的程序,你也許就明白了:

MyStructme[10]=

(

{1,2},{3,4},{5,6},億8},{9,10},{11,12},{13,14},{15,16},{17,18},{19,20}

};

intsum_value=sum(me,&MyStruct::value/10);

〃計(jì)算10個MyStruct結(jié)構(gòu)的value成員的總和:sum_value值為

110(2+4+6+8+...+20)intsum_key=sum(me/&MyStruct::key/10);

〃計(jì)算10個MyStruct結(jié)構(gòu)的key成員的總和:sum_key值為

100(1+3+5+7+...+19)

也許,你覺得用常規(guī)指針也可以做到,而且更易懂.Ok,沒問題:

intsum(MyStruct*objs,intcount)

(

intresult=O;

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

result+=objs[i].value;

returnresult;

)

你是想這么做嗎?但這么做,你只能計(jì)算value,如果要算key的話,

你要多寫一個函數(shù).有多少個成員需要計(jì)算的話,

你就要寫多少個函數(shù),多麻煩啊.

2、參數(shù)傳遞的問題:

可以相當(dāng)于隱式的返回值,可以返回更多的值:

#include"iostream.h"

voidexample(int*al,int&bl/intcl)

(

*al*=3;

++bl;

++cl;

}

voidmain()

int*a;

intb,c;

*a=6;

b=7;c=10;

example(a,b,c);

cout<<"*a="<<*a<

cout<<"b="<

cout<<"c="<

}

輸出:*a=18

b=8

c=10

注意到?jīng)]有,*a和b的值都改變了,而c沒有變.這是由于al是指

向*a(=6)的指針,也即與a是指向同一個地址,

所以當(dāng)al指向的值改變了,*a的值也就改變了.在函數(shù)中的參數(shù)

使用了引用(int&bl),bl是b的別名,也可以

把它當(dāng)作特殊的指針來理解,所以b的值會改變.函數(shù)中的參數(shù)

intel只是在函數(shù)中起作用,當(dāng)函數(shù)結(jié)束時(shí)候

便消失了,所以在main。中不起作用.

3、全局變量和局部變量的問題:

#include"iostream.h"

inta=5;

int*examplel(intb)

(

a+=b;

return&a;

}

int*example2(intb)

(

intc=5;

b+=c;

return&b;

}

voidmain()

(

int*al=examplel(10);

int*bl=example2(10);

cout<<"al="<<*al<

cout<<"bl="<<*bl<

}

輸出結(jié)果:

al=15

bl=4135

*bl怎么會是4135,而不是15呢?

由于a是全局變量,存放在全局變量的內(nèi)存區(qū),它一直是存在的;而

局部變量則是存在于函數(shù)的棧區(qū),當(dāng)函數(shù)

example2()調(diào)用結(jié)束后便消失,使b指向了一個不確定的區(qū)域,產(chǎn)

生指針懸掛.

4、內(nèi)存問題:

使用指針過程中應(yīng)該給變量一個適當(dāng)?shù)目臻g,以免產(chǎn)生不可見的

錯誤.請看以下代碼:#include"iostream.h"

voidmain()

(

char*al;

char*a2;

cin>>al;

cin>>a2;

cout<<"al="<

cout<<"a2="<

)

輸入:abc

123

輸出:

al=123

a2=

Nullpointerassignment

指針指向了"空".解決辦法就是分配適當(dāng)?shù)膬?nèi)存給這兩個字符串.

修正后的代碼如下:#include'ostream.h"

voidmain()

(

char*al;

char*a2;

al=newchar[10];

a2=newchar[10];

cin>>al;

cin>>a2;

cout<<"al="<

cout<<"a2="<

delete(a1);別忘了釋放內(nèi)存空間

delete(a2);

}

C語言所有復(fù)雜的指針聲明,都是由各種聲明嵌套構(gòu)成的.如何解

讀復(fù)雜指針聲明呢?右左法

則是一個既著名又常用的方法.不過,右左法則其實(shí)并不是c標(biāo)準(zhǔn)

里面的內(nèi)容,它是從C標(biāo)準(zhǔn)的聲明規(guī)定中歸納出來的方法.C標(biāo)準(zhǔn)的聲

明規(guī)則,是用來解決如何創(chuàng)建聲明的,而右左法則是用來解決如何辯識

一個聲明的,兩者可以說是相反的.右左法則的英文原文是這樣說

^:Theright-leftrule:Startreadingthedeclarationfromtheinnermostparent

heses^oright^andthengoleft.Whenyouencounterparentheses^thedirectio

nshoul

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論