




版權(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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年企業(yè)信息管理系統(tǒng)合同模板
- 2025年勘察分包合同參考文本
- 2025年交通運(yùn)輸工程總承包合同標(biāo)準(zhǔn)
- 2025年度新建住宅購買策劃合同協(xié)議
- 2025年企業(yè)軟件購置合同協(xié)議書
- 2025年企業(yè)勞動合同樣本協(xié)議
- 2025年企業(yè)市場營銷人員勞動合同格式
- 砂石料銷售合同補(bǔ)充協(xié)議模板5篇
- 2025年危險(xiǎn)品運(yùn)輸行業(yè)駕駛員雇傭合同范本
- 2025年全球出口銷售合同標(biāo)準(zhǔn)模板
- 食品銷售監(jiān)督管理工作培訓(xùn)
- 產(chǎn)品過程特殊特性初始清單(示例)
- 兩篇古典英文版成語故事塞翁失馬
- 中國古代文學(xué)史 馬工程課件(中)13第五編 宋代文學(xué) 第一章 北宋初期文學(xué)
- GB/T 14643.4-2009工業(yè)循環(huán)冷卻水中菌藻的測定方法第4部分:土壤真菌的測定平皿計(jì)數(shù)法
- DL-T 5190.1-2022 電力建設(shè)施工技術(shù)規(guī)范 第1部分:土建結(jié)構(gòu)工程(附條文說明)
- GA/T 914-2010聽力障礙的法醫(yī)學(xué)評定
- GA/T 642-2020道路交通事故車輛安全技術(shù)檢驗(yàn)鑒定
- 建筑工地生活區(qū)管理制度范本
- 屠宰站安全生產(chǎn)三項(xiàng)制度(安全生產(chǎn)責(zé)任制、制度、操作規(guī)程)匯編
- 【高等數(shù)學(xué)(工專)練習(xí)題】上海大學(xué)(悉尼工商學(xué)院)2022年真題測驗(yàn)匯總(附答案解析)
評論
0/150
提交評論