青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

FreeBird

C++博客 聯(lián)系 聚合 管理
  1 Posts :: 1 Stories :: 1 Comments :: 0 Trackbacks
/*
 *文章標(biāo)題:深入理解C語言指針的奧秘
 *原 作 者:不祥
 *原 出 處:vcer
 *發(fā) 布 者:自由鳥
 *發(fā)布類型:轉(zhuǎn)載
 *發(fā)布日期:2007-12-31
 */
指針是一個特殊的變量,它里面存儲的數(shù)值被解釋成為內(nèi)存里的一個地址。 要搞清一個指針需要搞清指針的四方面的內(nèi)容:指針的類型,指針?biāo)赶虻?類型,指針的值或者叫指針?biāo)赶虻膬?nèi)存區(qū),還有指針本身所占據(jù)的內(nèi)存區(qū)。讓我們分別說明。

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

例一:

(1)int*ptr;

(2)char*ptr;

(3)int**ptr;

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

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

指針的類型

從語法的角度看,你只要把指針聲明語句里的指針名字去掉,剩下的部分就是這個指針的類型。這是指針本身所具有的類型。讓我們看看例一中各個指針的類型:

(1)int*ptr;//指針的類型是int*

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

(3)int**ptr;//指針的類型是int**

(4)int(*ptr)[3];//指針的類型是int(*)[3]

(5)int*(*ptr)[4];//指針的類型是int*(*)[4]

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

指針?biāo)赶虻念愋?

當(dāng)你通過指針來訪問指針?biāo)赶虻膬?nèi)存區(qū)時,指針?biāo)赶虻念愋蜎Q定了編譯器將把那片內(nèi)存區(qū)里的內(nèi)容當(dāng)做什么來看待。

從語法上看,你只須把指針聲明語句中的指針名字和名字左邊的指針聲明符*去掉,剩下的就是指針?biāo)赶虻念愋汀@纾?

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

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

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

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

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

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

指針的類型(即指針本身的類型)和指針?biāo)赶虻念愋褪莾蓚€概念。當(dāng)你對C越來越熟悉時,你會發(fā)現(xiàn),把與指針攪和在一起的"類型"這個概念分成"指針的類型"和"指針?biāo)赶虻念愋?兩個概念,是精通指針的關(guān)鍵點之一。我看了不少書,發(fā)現(xiàn)有些寫得差的書中,就把指針的這兩個概念攪在一起了,所以看起書來前后矛盾,越看越糊涂。

指針的值,或者叫指針?biāo)赶虻膬?nèi)存區(qū)或地址。指針的值是指針本身存儲的數(shù)值,這個值將被編譯器當(dāng)作一個地址,而不是一個一般的數(shù)值。在32位程序里,所有類型的指針的值都是一個32位整數(shù),因為32位程序里內(nèi)存地址全都是32位長。 指針?biāo)赶虻膬?nèi)存區(qū)就是從指針的值所代表的那個內(nèi)存地址開始,長度為si zeof(指針?biāo)赶虻念愋?的一片內(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)該問問:這個指針的類型是什么?指針指的類型是什么?該指針指向了哪里?

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

指針本身占了多大的內(nèi)存?你只要用函數(shù)sizeof(指針的類型)測一下就知道了。在32位平臺里,指針本身占據(jù)了4個字節(jié)的長度。

指針本身占據(jù)的內(nèi)存這個概念在判斷一個指針表達(dá)式是否是左值時很有用。

指針的算術(shù)運算

指針可以加上或減去一個整數(shù)。指針的這種運算的意義和通常的數(shù)值的加減運算的意義是不一樣的。例如:

例二:

1、chara[20];

2、int*ptr=a;

...

...

3、ptr++;

在上例中,指針ptr的類型是int*,它指向的類型是int,它被初始化為指向整形變量a。接下來的第3句中,指針ptr被加了1,編譯器是這樣處理的:它把指針ptr的值加上了sizeof(int),在32位程序中,是被加上了4。由于地址是用字節(jié)做單位的,故ptr所指向的地址由原來的變量a的地址向高地址方向增加了4個字節(jié)。

由于char類型的長度是一個字節(jié),所以,原來ptr是指向數(shù)組a的第0號單元開始的四個字節(jié),此時指向了數(shù)組a中從第4號單元開始的四個字節(jié)。

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

例三:

intarray[20];

int*ptr=array;

...

//此處略去為整型數(shù)組賦值的代碼。

...

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

{

(*ptr)++;

ptr++;

}

這個例子將整型數(shù)組中各個單元的值加1。由于每次循環(huán)都將指針ptr加1,所以每次循環(huán)都能訪問數(shù)組的下一個單元。

再看例子:

例四:

1、chara[20];

2、int*ptr=a;

...

...

3、ptr+=5;

在這個例子中,ptr被加上了5,編譯器是這樣處理的:將指針ptr的值加上5乘sizeof(int),在32位程序中就是加上了5乘4=20。由于地址的單位是字節(jié),故現(xiàn)在的ptr所指向的地址比起加5后的ptr所指向的地址來說,向高地址方向移動了20個字節(jié)。在這個例子中,沒加5前的ptr指向數(shù)組a的第0號單元開始的四個字節(jié),加5后,ptr已經(jīng)指向了數(shù)組a的合法范圍之外了。雖然這種情況在應(yīng)用上會出問題,但在語法上卻是可以的。這也體現(xiàn)出了指針的靈活性。

如果上例中,ptr是被減去5,那么處理過程大同小異,只不過ptr的值是被減去5乘sizeof(int),新的ptr指向的地址將比原來的ptr所指向的地址向低地址方向移動了20個字節(jié)。

總結(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é)。

一個指針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é)。

運算符&和*

這里&是取地址運算符,*是...書上叫做"間接運算符"。

&a的運算結(jié)果是一個指針,指針的類型是a的類型加個*,指針?biāo)赶虻念愋褪莂的類型,指針?biāo)赶虻牡刂仿铮蔷褪莂的地址。

*p的運算結(jié)果就五花八門了。總之*p的結(jié)果是p所指向的東西,這個東西有這些特點:它的類型是p指向的類型,它所占用的地址是p所指向的地址。

例五:

inta=12;

intb;

int*p;

int**ptr;

p=&a;

//&a的結(jié)果是一個指針,類型是int*,指向的類型是int,指向的地址是a的地址。

*p=24;

//*p的結(jié)果,在這里它的類型是int,它所占用的地址是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所指向的東西,在這里是一個指針,對這個指針再做一次*運算,結(jié)果就是一個int類型的變量。

指針表達(dá)式

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

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

例六:

inta,b;

intarray[10];

int*pa;

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

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

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

pa=array;

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

例七:

char*arr[20];

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

char*str;

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

tr=*(parr+1);//*(parr+1)是指針表達(dá)式

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

由于指針表達(dá)式的結(jié)果是一個指針,所以指針表達(dá)式也具有指針?biāo)哂械乃膫€要素:指針的類型,指針?biāo)赶虻念愋停羔樦赶虻膬?nèi)存區(qū),指針自身占據(jù)的內(nèi)存。

好了,當(dāng)一個指針表達(dá)式的結(jié)果指針已經(jīng)明確地具有了指針自身占據(jù)的內(nèi)存的話,這個指針表達(dá)式就是一個左值,否則就不是一個左值。

在例七中,&a不是一個左值,因為它還沒有占據(jù)明確的內(nèi)存。*ptr是一個左值,因為*ptr這個指針已經(jīng)占據(jù)了內(nèi)存,其實*ptr就是指針pa,既然pa已經(jīng)在內(nèi)存中有了自己的位置,那么*ptr當(dāng)然也有了自己的位置。

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

數(shù)組的數(shù)組名其實可以看作一個指針。看下例:

例八:

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

...

...

value=array[0];//也可寫成:value=*array;

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

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

上例中,一般而言數(shù)組名array代表數(shù)組本身,類型是int[10],但如果把array看做指針的話,它指向數(shù)組的第0個單元,類型是int*,所指向的類型是數(shù)組單元的類型即int。因此*array等于0就一點也不奇怪了。同理,array+3是一個指向數(shù)組第3個單元的指針,所以*(array+3)等于3。其它依此類推。

例九:

char*str[3]={

"Hello,thisisasample!",

"Hi,goodmorning.",

"Helloworld"

};

chars[80];

trcpy(s,str[0]);//也可寫成strcpy(s,*str);

trcpy(s,str[1]);//也可寫成strcpy(s,*(str+1));

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

上例中,str是一個三單元的數(shù)組,該數(shù)組的每個單元都是一個指針,這些指針各指向一個字符串。把指針數(shù)組名str當(dāng)作一個指針的話,它指向數(shù)組的第0號單元,它的類型是char**,它指向的類型是char*。

*str也是一個指針,它的類型是char*,它所指向的類型是char,它指向的地址是字符串"Hello,thisisasample!"的第一個字符的地址,即’H’的地址。 str+1也是一個指針,它指向數(shù)組的第1號單元,它的類型是char**,它指向的類型是char*。

*(str+1)也是一個指針,它的類型是char*,它所指向的類型是char,它指向 "Hi,goodmorning."的第一個字符’H’,等等。

下面總結(jié)一下數(shù)組的數(shù)組名的問題。聲明了一個數(shù)組TYPEarray[n],則數(shù)組名稱array就有了兩重含義:第一,它代表整個數(shù)組,它的類型是TYPE[n];第二 ,它是一個指針,該指針的類型是TYPE*,該指針指向的類型是TYPE,也就是數(shù)組單元的類型,該指針指向的內(nèi)存區(qū)就是數(shù)組第0號單元,該指針自己占有單獨的內(nèi)存區(qū),注意它和數(shù)組第0號單元占據(jù)的內(nèi)存區(qū)是不同的。該指針的值是不能修改的,即類似array++的表達(dá)式是錯誤的。

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

在表達(dá)式sizeof(array)中,數(shù)組名array代表數(shù)組本身,故這時sizeof函數(shù)測出的是整個數(shù)組的大小。

在表達(dá)式*array中,array扮演的是指針,因此這個表達(dá)式的結(jié)果就是數(shù)組第0號單元的值。sizeof(*array)測出的是數(shù)組單元的大小。

表達(dá)式array+n(其中n=0,1,2,....。)中,array扮演的是指針,故array+n的結(jié)果是一個指針,它的類型是TYPE*,它指向的類型是TYPE,它指向數(shù)組第n號單元。故sizeof(array+n)測出的是指針類型的大小。

例十

intarray[10];

int(*ptr)[10];

ptr=&array;:

上例中ptr是一個指針,它的類型是int(*)[10],他指向的類型是int[10] ,我們用整個數(shù)組的首地址來初始化它。在語句ptr=&array中,array代表數(shù)組本身。

本節(jié)中提到了函數(shù)sizeof(),那么我來問一問,sizeof(指針名稱)測出的究竟是指針自身類型的大小呢還是指針?biāo)赶虻念愋偷拇笮。看鸢甘乔罢摺@纾?

int(*ptr)[10];

則在32位程序中,有:

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

izeof(int[10])==40

izeof(ptr)==4

實際上,sizeof(對象)測出的都是對象自身的類型的大小,而不是別的什么類型的大小。

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

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

例十一:

tructMyStruct

{

inta;

intb;

intc;

}

MyStructss={20,30,40};

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

MyStruct*ptr=&ss;

//聲明了一個指向結(jié)構(gòu)對象ss的指針。它的類型是MyStruct*,它指向的類型是MyStruct。

int*pstr=(int*)&ss;

//聲明了一個指向結(jié)構(gòu)對象ss的指針。但是它的類型和它指向的類型和ptr是不同的。

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

答案:

ptr->a;

ptr->b;

ptr->c;

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

答案:

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

*(pstr+1);//訪問了ss的成員b。

*(pstr+2)//訪問了ss的成員c。

雖然我在我的MSVC++6.0上調(diào)式過上述代碼,但是要知道,這樣使用pstr來訪問結(jié)構(gòu)成員是不正規(guī)的,為了說明為什么不正規(guī),讓我們看看怎樣通過指針來訪問數(shù)組的各個單元:

例十二:

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

int*pa=array;

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

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

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

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

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

所有的C/C++編譯器在排列數(shù)組的單元時,總是把各個數(shù)組單元存放在連續(xù)的存儲區(qū)里,單元和單元之間沒有空隙。但在存放結(jié)構(gòu)對象的各個成員時,在某種編譯環(huán)境下,可能會需要字對齊或雙字對齊或者是別的什么對齊,需要在相鄰兩個成員之間加若干個"填充字節(jié)",這就導(dǎo)致各個成員之間可能會有若干個字節(jié)的空隙。

所以,在例十二中,即使*pstr訪問到了結(jié)構(gòu)對象ss的第一個成員變量a,也不能保證*(pstr+1)就一定能訪問到結(jié)構(gòu)成員b。因為成員a和成員b之間可能會有若干填充字節(jié),說不定*(pstr+1)就正好訪問到了這些填充字節(jié)呢。這也證明了指針的靈活性。要是你的目的就是想看看各個結(jié)構(gòu)成員之間到底有沒有填充字節(jié),嘿,這倒是個不錯的方法。

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

指針和函數(shù)的關(guān)系

可以把一個指針聲明成為一個指向函數(shù)的指針。intfun1(char*,int);

int(*pfun1)(char*,int);

pfun1=fun1;

....

....

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

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

例十三:

intfun(char*);

inta;

charstr[]="abcdefghijklmn";

a=fun(str);

...

...

intfun(char*s)

{

intnum=0;

for(inti=0;i{

num+=*s;s++;

}

returnnum;

}

這個例子中的函數(shù)fun統(tǒng)計一個字符串中各個字符的ASCII碼值之和。前面說了,數(shù)組的名字也是一個指針。在函數(shù)調(diào)用中,當(dāng)把str作為實參傳遞給形參s后,實際是把str的值傳遞給了s,s所指向的地址就和str所指向的地址一致,但是str和s各自占用各自的存儲空間。在函數(shù)體內(nèi)對s進(jìn)行自加1運算,并不意味著同時對str進(jìn)行了自加1運算。

指針類型轉(zhuǎn)換

當(dāng)我們初始化一個指針或給一個指針賦值時,賦值號的左邊是一個指針,賦值號的右邊是一個指針表達(dá)式。在我們前面所舉的例子中,絕大多數(shù)情況下,指針的類型和指針表達(dá)式的類型是一樣的,指針?biāo)赶虻念愋秃椭羔槺磉_(dá)式所指向的類型是一樣的。

例十四:

1、floatf=12.3;

2、float*fptr=&f;

3、int*p;

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

p=&f;

不對。因為指針p的類型是int*,它指向的類型是int。表達(dá)式&f的結(jié)果是一個指針,指針的類型是float*,它指向的類型是float。兩者不一致,直接賦值的方法是不行的。至少在我的MSVC++6.0上,對指針的賦值語句要求賦值號兩邊的類型一致,所指向的類型也一致,其它的編譯器上我沒試過,大家可以試試。為了實現(xiàn)我們的目的,需要進(jìn)行"強制類型轉(zhuǎn)換":

p=(int*)&f;

如果有一個指針p,我們需要把它的類型和所指向的類型改為TYEP*TYPE, 那么語法格式是:

(TYPE*)p;

這樣強制類型轉(zhuǎn)換的結(jié)果是一個新指針,該新指針的類型是TYPE*,它指向的類型是TYPE,它指向的地址就是原指針指向的地址。而原來的指針p的一切屬性都沒有被修改。

一個函數(shù)如果使用了指針作為形參,那么在函數(shù)調(diào)用語句的實參和形參的結(jié)合過程中,也會發(fā)生指針類型的轉(zhuǎn)換。

例十五:

voidfun(char*);

inta=125,b;

fun((char*)&a);

...

...

voidfun(char*s)

{

charc;

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

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

}

}

注意這是一個32位程序,故int類型占了四個字節(jié),char類型占一個字節(jié)。函數(shù)fun的作用是把一個整數(shù)的四個字節(jié)的順序來個顛倒。注意到了嗎?在函數(shù)調(diào)用語句中,實參&a的結(jié)果是一個指針,它的類型是int*,它指向的類型是int。形參這個指針的類型是char*,它指向的類型是char。這樣,在實參和形參的結(jié)合過程中,我們必須進(jìn)行一次從int*類型到char*類型的轉(zhuǎn)換。結(jié)合這個例子,我們可以這樣來想象編譯器進(jìn)行轉(zhuǎn)換的過程:編譯器先構(gòu)造一個臨時指針char*temp, 然后執(zhí)行temp=(char*)&a,最后再把temp的值傳遞給s。所以最后的結(jié)果是:s的類型是char*,它指向的類型是char,它指向的地址就是a的首地址。

我們已經(jīng)知道,指針的值就是指針指向的地址,在32位程序中,指針的值其實是一個32位整數(shù)。那可不可以把一個整數(shù)當(dāng)作指針的值直接賦給指針呢?就象下面的語句:

unsignedinta;

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

...

...

a=20345686;

ptr=20345686;//我們的目的是要使指針ptr指向地址20345686(十進(jìn)制

ptr=a;//我們的目的是要使指針ptr指向地址20345686(十進(jìn)制)

編譯一下吧。結(jié)果發(fā)現(xiàn)后面兩條語句全是錯的。那么我們的目的就不能達(dá)到了嗎?不,還有辦法:

unsignedinta;

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

...

...

a=某個數(shù),這個數(shù)必須代表一個合法的地址;

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

嚴(yán)格說來這里的(TYPE*)和指針類型轉(zhuǎn)換中的(TYPE*)還不一樣。這里的(TYPE*)的意思是把無符號整數(shù)a的值當(dāng)作一個地址來看待。上面強調(diào)了a的值必須代表一個合法的地址,否則的話,在你使用ptr的時候,就會出現(xiàn)非法操作錯誤。

想想能不能反過來,把指針指向的地址即指針的值當(dāng)作一個整數(shù)取出來。完 全可以。下面的例子演示了把一個指針的值當(dāng)作一個整數(shù)取出來,然后再把這個整數(shù)當(dāng)作一個地址賦給一個指針:

例十六:

inta=123,b;

int*ptr=&a;

char*str;

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

tr=(char*)b;//把這個整數(shù)的值當(dāng)作一個地址賦給指針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é)的值被改變了!這會造成崩潰性的錯誤。

讓我們再來看一例:

例十八:

1、chara;

2、int*ptr=&a;

...

...

3、ptr++;

4、*ptr=115;

該例子完全可以通過編譯,并能執(zhí)行。但是看到?jīng)]有?第3句對指針ptr進(jìn)行自加1運算后,ptr指向了和整形變量a相鄰的高地址方向的一塊存儲區(qū)。這塊存儲區(qū)里是什么?我們不知道。有可能它是一個非常重要的數(shù)據(jù),甚至可能是一條代碼。而第4句竟然往這片存儲區(qū)里寫入一個數(shù)據(jù)!這是嚴(yán)重的錯誤。所以在使用指針時,程序員心里必須非常清楚:我的指針究竟指向了哪里。在用指針訪問數(shù)組的時候,也要注意不要超出數(shù)組的低端和高端界限,否則也會造成類似的錯誤。

在指針的強制類型轉(zhuǎn)換:ptr1=(TYPE*)ptr2中,如果sizeof(ptr2的類型)大于sizeof(ptr1的類型),那么在使用指針ptr1來訪問ptr2所指向的存儲區(qū)時是安全的。如果sizeof(ptr2的類型)小于sizeof(ptr1的類型),那么在使用指針ptr1來訪問ptr2所指向的存儲區(qū)時是不安全的。至于為什么,讀者結(jié)合例十七來想一想,應(yīng)該會明白的。

回復(fù):

摘錄的別人的:

C語言所有復(fù)雜的指針聲明,都是由各種聲明嵌套構(gòu)成的。如何解讀復(fù)雜指針聲明呢?右左法則是一個既著名又常用的方法。不過,右左法則其實并不是C標(biāo)準(zhǔn)里面的內(nèi)容,它是從C標(biāo)準(zhǔn)的聲明規(guī)定中歸納出來的方法。C標(biāo)準(zhǔn)的聲明規(guī)則,是用來解決如何創(chuàng)建聲明的,而右左法則是用來解決如何辯識一個聲明的,兩者可以說是相反的。右左法則的英文原文是這樣說的:

The right-left rule: Start reading the declaration from the innermost parentheses, go right, and then go left. When you encounter parentheses, the direction should be reversed. Once everything in the parentheses has been parsed, jump out of it. Continue till the whole declaration has been parsed.

這段英文的翻譯如下:

右左法則:首先從最里面的圓括號看起,然后往右看,再往左看。每當(dāng)遇到圓括號時,就應(yīng)該掉轉(zhuǎn)閱讀方向。一旦解析完圓括號里面所有的東西,就跳出圓括號。重復(fù)這個過程直到整個聲明解析完畢。

筆者要對這個法則進(jìn)行一個小小的修正,應(yīng)該是從未定義的標(biāo)識符開始閱讀,而不是從括號讀起,之所以是未定義的標(biāo)識符,是因為一個聲明里面可能有多個標(biāo)識符,但未定義的標(biāo)識符只會有一個。

現(xiàn)在通過一些例子來討論右左法則的應(yīng)用,先從最簡單的開始,逐步加深:

int (*func)(int *p);

首先找到那個未定義的標(biāo)識符,就是func,它的外面有一對圓括號,而且左邊是一個*號,這說明func是一個指針,然后跳出這個圓括號,先看右邊,也是一個圓括號,這說明(*func)是一個函數(shù),而func是一個指向這類函數(shù)的指針,就是一個函數(shù)指針,這類函數(shù)具有int*類型的形參,返回值類型是int。

int (*func)(int *p, int (*f)(int*));

func被一對括號包含,且左邊有一個*號,說明func是一個指針,跳出括號,右邊也有個括號,那么func是一個指向函數(shù)的指針,這類函數(shù)具有int *和int (*)(int*)這樣的形參,返回值為int類型。再來看一看func的形參int (*f)(int*),類似前面的解釋,f也是一個函數(shù)指針,指向的函數(shù)具有int*類型的形參,返回值為int。

int (*func[5])(int *p);

func右邊是一個[]運算符,說明func是一個具有5個元素的數(shù)組,func的左邊有一個*,說明func的元素是指針,要注意這里的*不是修飾func的,而是修飾func[5]的,原因是[]運算符優(yōu)先級比*高,func先跟[]結(jié)合,因此*修飾的是func[5]。跳出這個括號,看右邊,也是一對圓括號,說明func數(shù)組的元素是函數(shù)類型的指針,它所指向的函數(shù)具有int*類型的形參,返回值類型為int。

int (*(*func)[5])(int *p);

func被一個圓括號包含,左邊又有一個*,那么func是一個指針,跳出括號,右邊是一個[]運算符號,說明func是一個指向數(shù)組的指針,現(xiàn)在往左看,左邊有一個*號,說明這個數(shù)組的元素是指針,再跳出括號,右邊又有一個括號,說明這個數(shù)組的元素是指向函數(shù)的指針。總結(jié)一下,就是:func是一個指向數(shù)組的指針,這個數(shù)組的元素是函數(shù)指針,這些指針指向具有int*形參,返回值為int類型的函數(shù)。

int (*(*func)(int *p))[5];

func是一個函數(shù)指針,這類函數(shù)具有int*類型的形參,返回值是指向數(shù)組的指針,所指向的數(shù)組的元素是具有5個int元素的數(shù)組。

要注意有些復(fù)雜指針聲明是非法的,例如:

int func(void) [5];

func是一個返回值為具有5個int元素的數(shù)組的函數(shù)。但C語言的函數(shù)返回值不能為數(shù)組,這是因為如果允許函數(shù)返回值為數(shù)組,那么接收這個數(shù)組的內(nèi)容的東西,也必須是一個數(shù)組,但C語言的數(shù)組名是一個右值,它不能作為左值來接收另一個數(shù)組,因此函數(shù)返回值不能為數(shù)組。

int func[5](void);

func是一個具有5個元素的數(shù)組,這個數(shù)組的元素都是函數(shù)。這也是非法的,因為數(shù)組的元素除了類型必須一樣外,每個元素所占用的內(nèi)存空間也必須相同,顯然函數(shù)是無法達(dá)到這個要求的,即使函數(shù)的類型一樣,但函數(shù)所占用的空間通常是不相同的。

作為練習(xí),下面列幾個復(fù)雜指針聲明給讀者自己來解析,答案放在第十章里。

int (*(*func)[5][6])[7][8];

int (*(*(*func)(int *))[5])(int *);

int (*(*func[7][8][9])(int*))[5];

??????? 實際當(dāng)中,需要聲明一個復(fù)雜指針時,如果把整個聲明寫成上面所示的形式,對程序可讀性是一大損害。應(yīng)該用typedef來對聲明逐層分解,增強可讀性,例如對于聲明:

int (*(*func)(int *p))[5];

可以這樣分解:

typedef int (*PARA)[5];

typedef PARA (*func)(int *);

這樣就容易看得多了。

際當(dāng)中,需要聲明一個復(fù)雜指針時,如果把整個聲明寫成上面所示的形式,對程序可讀性是一大損害。應(yīng)該用typedef來對聲明逐層分解,增強可讀性,例如對于聲明:

int (*(*func)(int *p))[5];

可以這樣分解:

typedef int (*PARA)[5];

typedef PARA (*func)(int *);

這個比較有實際, 其它那么復(fù)雜的定義在現(xiàn)實使用中一點意義都沒有,屬寫編譯器的人研究的問題 ,如以下

int (*(*func)[5][6])[7][8];

int (*(*(*func)(int *))[5])(int *);

int (*(*func[7][8][9])(int*))[5];

posted on 2007-12-31 11:45 自由鳥 閱讀(114) 評論(0)  編輯 收藏 引用 所屬分類: C/C++

只有注冊用戶登錄后才能發(fā)表評論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            一本久久a久久精品亚洲| 狠狠久久婷婷| 欧美日本国产精品| 国产一区二区主播在线| 亚洲一区二区三区四区在线观看 | 久久视频一区二区| 欧美日韩一区二区三区在线 | 午夜精品久久久久久久久久久 | 久久久国产精彩视频美女艺术照福利| 欧美电影在线免费观看网站| 亚洲人成在线观看一区二区| 欧美激情精品| 欧美日韩亚洲国产一区| 亚洲男人影院| 在线综合亚洲| 亚洲一区二区三区在线播放| 国产精品日韩久久久久| 亚洲免费中文| 亚洲专区一区| 最近中文字幕日韩精品| 欧美一区激情| 亚洲乱码国产乱码精品精98午夜| 欧美性视频网站| 欧美一区二区免费观在线| 久久精精品视频| 亚洲视频电影图片偷拍一区| 亚洲欧美在线磁力| 一区二区免费在线观看| 羞羞视频在线观看欧美| 在线视频国产日韩| 亚洲欧洲av一区二区| 在线综合+亚洲+欧美中文字幕| 欧美伊人久久久久久久久影院| 亚洲狼人综合| 欧美成人一二三| 免费看的黄色欧美网站| 欧美精品成人91久久久久久久| 欧美专区在线观看一区| 国产精品二区三区四区| 欧美黑人国产人伦爽爽爽| 国产精品亚洲综合色区韩国| 亚洲一区免费视频| 午夜天堂精品久久久久 | 久久国产精品毛片| 亚洲午夜在线观看| 欧美高清在线| 中文欧美在线视频| 久久久精品欧美丰满| 国产精品黄色在线观看| 亚洲视频成人| 欧美激情精品久久久久久黑人| 欧美jizz19性欧美| 9人人澡人人爽人人精品| 亚洲国产精品一区二区尤物区| 久久精品国产999大香线蕉| 久久精品在线观看| 在线观看国产日韩| 欧美日韩久久不卡| 亚洲影院色无极综合| 久久爱www久久做| 亚洲国产精品嫩草影院| 欧美亚一区二区| 久久综合九色| 一区二区不卡在线视频 午夜欧美不卡在 | 久久久久久亚洲精品杨幂换脸| 国产专区欧美专区| 久久婷婷成人综合色| 亚洲图片欧美午夜| 欧美aⅴ一区二区三区视频| 99精品欧美一区二区蜜桃免费| 欧美日韩小视频| 欧美88av| 欧美一区二区三区久久精品| 免费看精品久久片| 久久精品夜色噜噜亚洲aⅴ| 亚洲激情小视频| 国产综合色在线| 国产日韩一区| 国产精品一区二区黑丝| 蜜桃伊人久久| 久久精品国产亚洲精品 | 久久精品动漫| 女女同性精品视频| 在线亚洲美日韩| 亚洲视频碰碰| 性欧美xxxx大乳国产app| 亚洲电影免费在线| 国产三区二区一区久久| 欧美日韩一二区| 国产精品你懂的在线| 欧美人成免费网站| 欧美劲爆第一页| 国产精品久久久久久久一区探花 | 亚洲区在线播放| 亚洲国产精品一区二区三区| 亚洲高清中文字幕| 亚洲国产天堂久久综合网| 亚洲激情视频网站| 亚洲国产成人精品久久| 亚洲国产岛国毛片在线| 亚洲小视频在线观看| 亚洲欧美经典视频| 免费在线观看一区二区| 国产精品白丝jk黑袜喷水| 国产美女精品免费电影| 国产日韩一区二区三区| 精品电影在线观看| 亚洲私人影院| 欧美成人国产| 亚洲午夜激情| 久久米奇亚洲| 国产欧美日韩视频| 一区二区三区四区五区精品视频| 亚洲女性喷水在线观看一区| 久久久亚洲精品一区二区三区| 亚洲精品少妇| 欧美不卡视频一区发布| 国产精品一二三视频| 日韩视频免费在线观看| 亚洲一区二三| 亚洲精品乱码久久久久久| 久久久久网址| 久久精品国产99| 亚洲精品一区二区三区四区高清| 久久精品亚洲| 国内精品一区二区| 久久亚洲色图| 久久国产精品一区二区| 国模叶桐国产精品一区| 蜜桃久久精品乱码一区二区| 亚洲精品国产精品国自产观看| 久久最新视频| 亚洲激情视频网| 亚洲国产一区二区三区在线播| 久久成人资源| 亚洲国产三级在线| 99综合电影在线视频| 国产精品日本欧美一区二区三区| 夜夜嗨av一区二区三区四区| 亚洲欧洲一区二区天堂久久| 免费一级欧美片在线观看| 一区二区精品在线观看| 午夜视频在线观看一区二区| 国产日韩久久| 日韩一二三区视频| 国产欧美日韩视频| 另类亚洲自拍| 国产精品青草久久| 欧美国产精品久久| 欧美屁股在线| 狂野欧美激情性xxxx欧美| 欧美激情综合五月色丁香| 亚洲风情亚aⅴ在线发布| 亚洲国产一区二区三区a毛片| 国产精品裸体一区二区三区| 久久综合久久久久88| 欧美成人中文字幕| 久久久久欧美| 国产女主播一区二区三区| 亚洲欧洲一区二区三区在线观看 | 国产精品免费网站| 亚洲国产日韩在线| 国模套图日韩精品一区二区| 日韩午夜电影av| 亚洲永久在线| 亚洲国产网站| 日韩视频永久免费观看| 欧美一区久久| 久久深夜福利免费观看| 国产一区二区三区免费不卡| 中日韩高清电影网| 午夜精品一区二区三区在线| 久久免费视频这里只有精品| 久久免费的精品国产v∧| 黑人巨大精品欧美一区二区| 久久国产精品久久精品国产| 久久理论片午夜琪琪电影网| 激情五月综合色婷婷一区二区| 亚洲综合丁香| 美玉足脚交一区二区三区图片| 在线观看国产欧美| 免费日韩成人| 欧美黑人一区二区三区| 亚洲精品欧美日韩专区| 国产精品卡一卡二| 久久国产精品一区二区| 亚洲精品乱码久久久久久黑人 | 一区二区三区久久精品| 一本综合久久| 国产一区二区三区av电影| 欧美电影在线| 欧美伊人久久| 中文精品在线| 日韩天堂在线观看| 浪潮色综合久久天堂| 亚洲午夜精品久久久久久浪潮| 在线看片一区| 国产三级欧美三级| 久久久久久久综合日本| 久久国产主播精品|