• <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>

            FreeBird

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

            先聲明幾個(gè)指針?lè)胖隼樱?

            例一:

            (1)int*ptr;

            (2)char*ptr;

            (3)int**ptr;

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

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

            指針的類型

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

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

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

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

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

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

            怎么樣?找出指針的類型的方法是不是很簡(jiǎn)單?

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

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

            從語(yǔ)法上看,你只須把指針聲明語(yǔ)句中的指針名字和名字左邊的指針聲明符*去掉,剩下的就是指針?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ù)運(yùn)算中,指針?biāo)赶虻念愋陀泻艽蟮淖饔谩?

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

            指針的值,或者叫指針?biāo)赶虻膬?nèi)存區(qū)或地址。指針的值是指針本身存儲(chǔ)的數(shù)值,這個(gè)值將被編譯器當(dāng)作一個(gè)地址,而不是一個(gè)一般的數(shù)值。在32位程序里,所有類型的指針的值都是一個(gè)32位整數(shù),因?yàn)?2位程序里內(nèi)存地址全都是32位長(zhǎng)。 指針?biāo)赶虻膬?nèi)存區(qū)就是從指針的值所代表的那個(gè)內(nèi)存地址開(kāi)始,長(zhǎng)度為si zeof(指針?biāo)赶虻念愋?的一片內(nèi)存區(qū)。以后,我們說(shuō)一個(gè)指針的值是XX,就相當(dāng)于說(shuō)該指針指向了以XX為首地址的一片內(nèi)存區(qū)域;我們說(shuō)一個(gè)指針指向了某塊內(nèi)存區(qū)域,就相當(dāng)于說(shuō)該指針的值是這塊內(nèi)存區(qū)域的首地址。

            指針?biāo)赶虻膬?nèi)存區(qū)和指針?biāo)赶虻念愋褪莾蓚€(gè)完全不同的概念。在例一中,指針?biāo)赶虻念愋鸵呀?jīng)有了,但由于指針還未初始化,所以它所指向的內(nèi)存區(qū)是不存在的,或者說(shuō)是無(wú)意義的。

            以后,每遇到一個(gè)指針,都應(yīng)該問(wèn)問(wèn):這個(gè)指針的類型是什么?指針指的類型是什么?該指針指向了哪里?

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

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

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

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

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

            例二:

            1、chara[20];

            2、int*ptr=a;

            ...

            ...

            3、ptr++;

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

            由于char類型的長(zhǎng)度是一個(gè)字節(jié),所以,原來(lái)ptr是指向數(shù)組a的第0號(hào)單元開(kāi)始的四個(gè)字節(jié),此時(shí)指向了數(shù)組a中從第4號(hào)單元開(kāi)始的四個(gè)字節(jié)。

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

            例三:

            intarray[20];

            int*ptr=array;

            ...

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

            ...

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

            {

            (*ptr)++;

            ptr++;

            }

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

            再看例子:

            例四:

            1、chara[20];

            2、int*ptr=a;

            ...

            ...

            3、ptr+=5;

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

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

            總結(jié)一下,一個(gè)指針ptrold加上一個(gè)整數(shù)n后,結(jié)果是一個(gè)新的指針ptrnew,ptrnew的類型和ptrold的類型相同,ptrnew所指向的類型和ptrold所指向的類型也相同。ptrnew的值將比ptrold的值增加了n乘sizeof(ptrold所指向的類型)個(gè)字節(jié)。就是說(shuō),ptrnew所指向的內(nèi)存區(qū)將比ptrold所指向的內(nèi)存區(qū)向高地址方向移動(dòng)了n乘sizeof(ptrold所指向的類型)個(gè)字節(jié)。

            一個(gè)指針ptrold減去一個(gè)整數(shù)n后,結(jié)果是一個(gè)新的指針ptrnew,ptrnew的類型和ptrold的類型相同,ptrnew所指向的類型和ptrold所指向的類型也相同。ptrnew的值將比ptrold的值減少了n乘sizeof(ptrold所指向的類型)個(gè)字節(jié),就是說(shuō),ptrnew所指向的內(nèi)存區(qū)將比ptrold所指向的內(nèi)存區(qū)向低地址方向移動(dòng)了n乘sizeof(ptrold所指向的類型)個(gè)字節(jié)。

            運(yùn)算符&和*

            這里&是取地址運(yùn)算符,*是...書(shū)上叫做"間接運(yùn)算符"。

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

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

            例五:

            inta=12;

            intb;

            int*p;

            int**ptr;

            p=&a;

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

            *p=24;

            //*p的結(jié)果,在這里它的類型是int,它所占用的地址是p所指向的地址,顯然,*p就是變量a。

            ptr=&p;

            //&p的結(jié)果是個(gè)指針,該指針的類型是p的類型加個(gè)*,在這里是int **。該指針?biāo)赶虻念愋褪莗的類型,這里是int*。該指針?biāo)赶虻牡刂肪褪侵羔榩自己的地址。

            *ptr=&b;

            //*ptr是個(gè)指針,&b的結(jié)果也是個(gè)指針,且這兩個(gè)指針的類型和所指向的類型是一樣的,所以用&b來(lái)給*ptr賦值就是毫無(wú)問(wèn)題的了。

            **ptr=34;

            //*ptr的結(jié)果是ptr所指向的東西,在這里是一個(gè)指針,對(duì)這個(gè)指針再做一次*運(yùn)算,結(jié)果就是一個(gè)int類型的變量。

            指針表達(dá)式

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

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

            例六:

            inta,b;

            intarray[10];

            int*pa;

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

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

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

            pa=array;

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

            例七:

            char*arr[20];

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

            char*str;

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

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

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

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

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

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

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

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

            例八:

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

            ...

            ...

            value=array[0];//也可寫(xiě)成:value=*array;

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

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

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

            例九:

            char*str[3]={

            "Hello,thisisasample!",

            "Hi,goodmorning.",

            "Helloworld"

            };

            chars[80];

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

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

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

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

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

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

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

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

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

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

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

            例十

            intarray[10];

            int(*ptr)[10];

            ptr=&array;:

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

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

            int(*ptr)[10];

            則在32位程序中,有:

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

            izeof(int[10])==40

            izeof(ptr)==4

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

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

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

            例十一:

            tructMyStruct

            {

            inta;

            intb;

            intc;

            }

            MyStructss={20,30,40};

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

            MyStruct*ptr=&ss;

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

            int*pstr=(int*)&ss;

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

            請(qǐng)問(wèn)怎樣通過(guò)指針ptr來(lái)訪問(wèn)ss的三個(gè)成員變量?

            答案:

            ptr->a;

            ptr->b;

            ptr->c;

            又請(qǐng)問(wèn)怎樣通過(guò)指針pstr來(lái)訪問(wèn)ss的三個(gè)成員變量?

            答案:

            *pstr;//訪問(wèn)了ss的成員a。

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

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

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

            例十二:

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

            int*pa=array;

            通過(guò)指針pa訪問(wèn)數(shù)組array的三個(gè)單元的方法是:

            *pa;//訪問(wèn)了第0號(hào)單元

            *(pa+1);//訪問(wèn)了第1號(hào)單元

            *(pa+2);//訪問(wèn)了第2號(hào)單元

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

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

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

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

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

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

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

            pfun1=fun1;

            ....

            ....

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

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

            例十三:

            intfun(char*);

            inta;

            charstr[]="abcdefghijklmn";

            a=fun(str);

            ...

            ...

            intfun(char*s)

            {

            intnum=0;

            for(inti=0;i{

            num+=*s;s++;

            }

            returnnum;

            }

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

            指針類型轉(zhuǎn)換

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

            例十四:

            1、floatf=12.3;

            2、float*fptr=&f;

            3、int*p;

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

            p=&f;

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

            p=(int*)&f;

            如果有一個(gè)指針p,我們需要把它的類型和所指向的類型改為T(mén)YEP*TYPE, 那么語(yǔ)法格式是:

            (TYPE*)p;

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

            一個(gè)函數(shù)如果使用了指針作為形參,那么在函數(shù)調(diào)用語(yǔ)句的實(shí)參和形參的結(jié)合過(guò)程中,也會(huì)發(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;

            }

            }

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

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

            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)后面兩條語(yǔ)句全是錯(cuò)的。那么我們的目的就不能達(dá)到了嗎?不,還有辦法:

            unsignedinta;

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

            ...

            ...

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

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

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

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

            例十六:

            inta=123,b;

            int*ptr=&a;

            char*str;

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

            tr=(char*)b;//把這個(gè)整數(shù)的值當(dāng)作一個(gè)地址賦給指針str。

            現(xiàn)在我們已經(jīng)知道了,可以把指針的值當(dāng)作一個(gè)整數(shù)取出來(lái),也可以把一個(gè)整數(shù)值當(dāng)作地址賦給一個(gè)指針。

            指針的安全問(wèn)題

            看下面的例子:

            例十七:

            chars=’a’;

            int*ptr;

            ptr=(int*)&s;

            *ptr=1298;

            指針ptr是一個(gè)int*類型的指針,它指向的類型是int。它指向的地址就是s的首地址。在32位程序中,s占一個(gè)字節(jié),int類型占四個(gè)字節(jié)。最后一條語(yǔ)句不但改變了s所占的一個(gè)字節(jié),還把和s相臨的高地址方向的三個(gè)字節(jié)也改變了。這三個(gè)字節(jié)是干什么的?只有編譯程序知道,而寫(xiě)程序的人是不太可能知道的。也許這三個(gè)字節(jié)里存儲(chǔ)了非常重要的數(shù)據(jù),也許這三個(gè)字節(jié)里正好是程序的一條代碼,而由于你對(duì)指針的馬虎應(yīng)用,這三個(gè)字節(jié)的值被改變了!這會(huì)造成崩潰性的錯(cuò)誤。

            讓我們?cè)賮?lái)看一例:

            例十八:

            1、chara;

            2、int*ptr=&a;

            ...

            ...

            3、ptr++;

            4、*ptr=115;

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

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

            回復(fù):

            摘錄的別人的:

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

            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.

            這段英文的翻譯如下:

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

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

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

            int (*func)(int *p);

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

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

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

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

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

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

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

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

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

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

            int func(void) [5];

            func是一個(gè)返回值為具有5個(gè)int元素的數(shù)組的函數(shù)。但C語(yǔ)言的函數(shù)返回值不能為數(shù)組,這是因?yàn)槿绻试S函數(shù)返回值為數(shù)組,那么接收這個(gè)數(shù)組的內(nèi)容的東西,也必須是一個(gè)數(shù)組,但C語(yǔ)言的數(shù)組名是一個(gè)右值,它不能作為左值來(lái)接收另一個(gè)數(shù)組,因此函數(shù)返回值不能為數(shù)組。

            int func[5](void);

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

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

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

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

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

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

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

            可以這樣分解:

            typedef int (*PARA)[5];

            typedef PARA (*func)(int *);

            這樣就容易看得多了。

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

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

            可以這樣分解:

            typedef int (*PARA)[5];

            typedef PARA (*func)(int *);

            這個(gè)比較有實(shí)際, 其它那么復(fù)雜的定義在現(xiàn)實(shí)使用中一點(diǎn)意義都沒(méi)有,屬寫(xiě)編譯器的人研究的問(wè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 自由鳥(niǎo) 閱讀(110) 評(píng)論(0)  編輯 收藏 引用 所屬分類: C/C++

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


            草草久久久无码国产专区| 国内精品久久久久久久久电影网| A狠狠久久蜜臀婷色中文网| 久久精品国产亚洲AV香蕉| 久久人人爽人人人人爽AV| 久久夜色精品国产网站| 国产激情久久久久影院老熟女免费| 国产精品99久久精品爆乳| 日韩久久无码免费毛片软件| 久久SE精品一区二区| 国内精品久久久久久久coent| 午夜视频久久久久一区| 久久国产精品久久国产精品| 亚洲精品午夜国产va久久| 99久久人妻无码精品系列| 久久久久亚洲国产| 精品综合久久久久久97超人 | 国内精品久久久久久久亚洲| 久久91精品国产91| 伊人色综合久久天天| 色综合久久久久综合体桃花网| 99久久99久久精品国产片果冻| 无码人妻少妇久久中文字幕蜜桃| 国产成人综合久久久久久| 国产成人久久AV免费| 中文字幕人妻色偷偷久久| 污污内射久久一区二区欧美日韩| 久久免费精品视频| 久久精品国产2020| 欧美日韩久久中文字幕| 久久丝袜精品中文字幕| yellow中文字幕久久网| 91久久福利国产成人精品| 久久99国产精品久久99| 久久亚洲中文字幕精品有坂深雪| 久久久久国产精品嫩草影院| 一本久久免费视频| 久久亚洲精品无码观看不卡| 久久久久亚洲AV综合波多野结衣| 国产午夜精品久久久久九九电影| 伊人久久大香线蕉影院95|