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

            右左法則----復(fù)雜指針解析

            首先看看如下一個(gè)聲明:

             

            int* ( *( *fun )( int* ) )[10];

             

            這是一個(gè)會(huì)讓初學(xué)者感到頭暈?zāi)垦!⒏械娇謶值暮瘮?shù)指針聲明。在熟練掌握C/C++的聲明語(yǔ)法之前,不學(xué)習(xí)一定的規(guī)則,想理解好這類復(fù)雜聲明是比較困難的。

             

            C/C++所有復(fù)雜的聲明結(jié)構(gòu),都是由各種聲明嵌套構(gòu)成的。如何解讀復(fù)雜指針聲明?右左法則是一個(gè)很著名、很有效的方法。不過(guò),右左法則其實(shí)并不是C/C++標(biāo)準(zhǔn)里面的內(nèi)容,它是從C/C++標(biāo)準(zhǔn)的聲明規(guī)定中歸納出來(lái)的方法。C/C++標(biāo)準(zhǔn)的聲明規(guī)則,是用來(lái)解決如何創(chuàng)建聲明的,而右左法則是用來(lái)解決如何辯識(shí)一個(gè)聲明的,從嵌套的角度看,兩者可以說(shuō)是一個(gè)相反的過(guò)程。右左法則的英文原文是這樣說(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í)符開始閱讀,而不是從括號(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)單的開始,逐步加深:

             

            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/C++語(yǔ)言的數(shù)組名是一個(gè)不可修改的左值,它不能直接被另一個(gè)數(shù)組的內(nèi)容修改,因此函數(shù)返回值不能為數(shù)組。

             

            int func[5](void);

             

            func是一個(gè)具有5個(gè)元素的數(shù)組,這個(gè)數(shù)組的元素都是函數(shù)。這也是非法的,因?yàn)閿?shù)組的元素必須是對(duì)象,但函數(shù)不是對(duì)象,不能作為數(shù)組的元素。

             

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

             

            typedef是一種聲明,但它聲明的不是變量,也沒(méi)有創(chuàng)建新類型,而是某種類型的別名。typedef有很大的用途,對(duì)一個(gè)復(fù)雜聲明進(jìn)行分解以增強(qiáng)可讀性是其作用之一。例如對(duì)于聲明:

             

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

             

            可以這樣分解:

             

            typedef  int (*PARA)[5];

            typedef PARA (*func)(int *);

             

            這樣就容易看得多了。

             

            typedef的另一個(gè)作用,是作為基于對(duì)象編程的高層抽象手段。在ADT中,它可以用來(lái)在C/C++和現(xiàn)實(shí)世界的物件間建立關(guān)聯(lián),將這些物件抽象成C/C++的類型系統(tǒng)。在設(shè)計(jì)ADT的時(shí)候,我們常常聲明某個(gè)指針的別名,例如:

             

            typedef struct node * list;

             

            ADT的角度看,這個(gè)聲明是再自然不過(guò)的事情,可以用list來(lái)定義一個(gè)列表。但從C/C++語(yǔ)法的角度來(lái)看,它其實(shí)是不符合C/C++聲明語(yǔ)法的邏輯的,它暴力地將指針聲明符從指針聲明器中分離出來(lái),這會(huì)造成一些異于人們閱讀習(xí)慣的現(xiàn)象,考慮下面代碼:

             

            const struct node *p1;

            typedef struct node *list;

            const list p2;

             

            p1類型是const struct node*,那么p2呢?如果你以為就是把list簡(jiǎn)單“代入”p2,然后得出p2類型也是const struct node*的結(jié)果,就大錯(cuò)特錯(cuò)了。p2的類型其實(shí)是struct node * const p2,那個(gè)const限定的是p2,不是node。造成這一奇異現(xiàn)象的原因是指針聲明器被分割,標(biāo)準(zhǔn)中規(guī)定:

             

            6.7.5.1 Pointer declarators

             

            Semantics

             

             If in the declaration ‘‘T D1’, D1 has the form

             

            * type-qualifier-listopt D

             

            and the type specified for ident in the declaration ‘‘T D’’ is

             

            ‘‘derived-declarator-type-list T’’

             

            then the type specified for ident is

             

            ‘‘derived-declarator-type-list type-qualifier-list pointer to T’’

             

            For each type qualifier in the list, ident is a so-qualified pointer.

             

            指針的聲明器由指針聲明符*、可選的類型限定詞type-qualifier-listopt和標(biāo)識(shí)符D組成,這三者在邏輯上是一個(gè)整體,構(gòu)成一個(gè)完整的指針聲明器。這也是多個(gè)變量同列定義時(shí)指針聲明符必須緊跟標(biāo)識(shí)符的原因,例如:

             

            int *p, q, *k;

             

            pk都是指針,但q不是,這是因?yàn)?/span>*p*k是一個(gè)整體指針聲明器,以表示聲明的是一個(gè)指針。編譯器會(huì)把指針聲明符左邊的類型包括其限定詞作為指針指向的實(shí)體的類型,右邊的限定詞限定被聲明的標(biāo)識(shí)符。但現(xiàn)在typedef struct node *list硬生生把*從整個(gè)指針聲明器中分離出來(lái),編譯器找不到*,會(huì)認(rèn)為const list p2中的const是限定p2的,正因如此,p2的類型是node * const而不是const node*

             

            雖然typedef struct node* list不符合聲明語(yǔ)法的邏輯,但基于typedefADT中的重要作用以及信息隱藏的要求,我們應(yīng)該讓用戶這樣使用list A,而不是list *A,因此在ADT的設(shè)計(jì)中仍應(yīng)使用上述typedef語(yǔ)法,但需要注意其帶來(lái)的不利影響。

            posted on 2016-04-14 13:49 sheng 閱讀(249) 評(píng)論(0)  編輯 收藏 引用


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


            導(dǎo)航

            <2018年11月>
            28293031123
            45678910
            11121314151617
            18192021222324
            2526272829301
            2345678

            統(tǒng)計(jì)

            常用鏈接

            留言簿(1)

            隨筆檔案

            收藏夾

            同行

            搜索

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            狠狠色丁香婷婷综合久久来| 日本久久久精品中文字幕| 久久综合色区| 久久亚洲AV无码精品色午夜麻豆| 一本色道久久综合狠狠躁篇| 久久99精品久久久大学生| 国产精品毛片久久久久久久| 久久九九久精品国产| 亚洲午夜久久久久久久久电影网| 99久久99久久久精品齐齐| 久久国产综合精品五月天| 久久精品蜜芽亚洲国产AV| 久久精品国产WWW456C0M| 奇米影视7777久久精品| 久久久中文字幕日本| 成人国内精品久久久久影院| 久久久午夜精品福利内容| 国产精品热久久无码av| 国内精品久久久久伊人av| 亚洲人AV永久一区二区三区久久| 久久精品视频免费| 国内精品伊人久久久久AV影院| 色8激情欧美成人久久综合电| 国内精品久久久久影院免费| 久久久免费精品re6| 久久只有这精品99| 久久久久久久综合狠狠综合| 久久国产高清一区二区三区| a级成人毛片久久| 久久精品视频网| 青青草国产精品久久久久| 99久久久国产精品免费无卡顿| 亚洲午夜久久久久妓女影院 | 精品国产日韩久久亚洲| 久久精品中文字幕一区| 久久久久国产| 人妻中文久久久久| 久久久久国产精品人妻| 无遮挡粉嫩小泬久久久久久久| 国产毛片欧美毛片久久久| 久久久噜噜噜久久熟女AA片|