• <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>
            隨筆-80  評(píng)論-24  文章-0  trackbacks-0

            C(和C++)中的宏(Macro)屬于編譯器預(yù)處理的范疇,屬于編譯期概念(而非運(yùn)行期概念)。下面對(duì)常遇到的宏的使用問(wèn)題做了簡(jiǎn)單總結(jié)。

            關(guān) 于#和##

            在C語(yǔ)言的宏中,#的功能是將其后面的宏參數(shù)進(jìn)行字符串化操作(Stringfication),簡(jiǎn)單說(shuō)就是在對(duì)它所引用的宏 變量通過(guò)替換后在其左右各加上一個(gè)雙引號(hào)。比如下面代碼中的宏:

            #define WARN_IF(EXP) \

            do{ if (EXP) \

            fprintf(stderr, "Warning: " #EXP "\n"); } \

            while(0)

            那么實(shí)際使用中會(huì)出現(xiàn)下面所示的替換過(guò)程:

            WARN_IF (divider == 0);


            被替換為


            do {

            if (divider == 0)

            fprintf(stderr, "Warning" "divider == 0" "\n");

            } while(0);

            這樣每次divider(除數(shù))為0的時(shí)候便會(huì)在標(biāo) 準(zhǔn)錯(cuò)誤流上輸出一個(gè)提示信息。

            而## 被稱為連接符(concatenator),用來(lái)將兩個(gè)Token連接為一個(gè)Token。注意這里連接的對(duì)象是Token就行,而不一定是宏的變量。比如 你要做一個(gè)菜單項(xiàng)命令名和函數(shù)指針組成的結(jié)構(gòu)體的數(shù)組,并且希望在函數(shù)名和菜單項(xiàng)命令名之間有直觀的、名字上的關(guān)系。那么下面的代碼就非常實(shí)用:

            struct command

            {

            char * name;

            void (*function) (void);

            };


            #define COMMAND(NAME) { NAME, NAME ## _command }


            // 然后你就用一些預(yù)先定義好的命令來(lái)方便的初始化一個(gè)command結(jié)構(gòu)的數(shù)組了:


            struct command commands[] = {

            COMMAND(quit),

            COMMAND(help),

            ...

            }

            COMMAND宏在這里充當(dāng)一個(gè)代碼生成器的作 用,這樣可以在一定程度上減少代碼密度,間接地也可以減少不留心所造成的錯(cuò)誤。我們還可以n個(gè)##符號(hào)連接 n+1個(gè)Token,這個(gè)特性也是#符號(hào)所不具備的。比如:

            #define LINK_MULTIPLE(a,b,c,d) a##_##b##_##c##_##d


            typedef struct _record_type LINK_MULTIPLE(name,company,position,salary);

            // 這里這個(gè)語(yǔ)句將展開(kāi)為:

            // typedef struct _record_type name_company_position_salary;

            關(guān) 于...的使用

            ...在C宏中稱為Variadic Macro,也就是變參宏。比如:

            #define myprintf(templt,...) fprintf(stderr,templt,__VA_ARGS__)


            // 或者


            #define myprintf(templt,args...) fprintf(stderr,templt,args)

            第 一個(gè)宏中由于沒(méi)有對(duì)變參起名,我們用默認(rèn)的宏__VA_ARGS__來(lái)替代它。第二個(gè)宏中,我們顯式地命名變參為args,那么我們?cè)诤甓x中就可以用 args來(lái)代指變參了。同C語(yǔ)言的stdcall一樣,變參必須作為參數(shù)表的最有一項(xiàng)出現(xiàn)。當(dāng)上面的宏中我們只能提供第一個(gè)參數(shù)templt時(shí),C標(biāo)準(zhǔn)要 求我們必須寫成:

            myprintf(templt,);

            的形式。這時(shí)的替換過(guò)程為:

            myprintf("Error!\n",);


            替換為:



            fprintf(stderr,"Error!\n",);

            這是一個(gè) 語(yǔ)法錯(cuò)誤,不能正常編譯。這個(gè)問(wèn)題一般有兩個(gè)解決方法。首先,GNU CPP提供的解決方法允許上面的宏調(diào)用寫成:

            myprintf(templt);

            而 它將會(huì)被通過(guò)替換變成:

            fprintf(stderr,"Error!\n",);

            很明顯,這里仍然會(huì)產(chǎn)生編譯錯(cuò)誤(非 本例的某些情況下不會(huì)產(chǎn)生編譯錯(cuò)誤)。除了這種方式外,c99和GNU CPP都支持下面的宏定義方式:

            #define myprintf(templt, ...) fprintf(stderr,templt, ##__VAR_ARGS__)

            這 時(shí),##這個(gè)連接符號(hào)充當(dāng)?shù)淖饔镁褪钱?dāng)__VAR_ARGS__為空的時(shí)候,消除前面的那個(gè)逗號(hào)。那么此時(shí)的翻譯過(guò)程如下:

            myprintf(templt);


            被轉(zhuǎn)化為:


            fprintf(stderr,templt);

            這樣如果templt合法,將不會(huì)產(chǎn)生 編譯錯(cuò)誤。 這里列出了一些宏使用中容易出錯(cuò)的地方,以及合適的使用方式。

            錯(cuò)誤的嵌套-Misnesting

            宏的定義不一定要有完整的、配對(duì)的括號(hào),但是為了避免出錯(cuò)并且提高可讀性,最好避免這樣使用。

            由 操作符優(yōu)先級(jí)引起的問(wèn)題-Operator Precedence Problem

            由于宏只是簡(jiǎn)單的替換,宏的參數(shù)如果是復(fù)合結(jié)構(gòu),那么 通過(guò)替換之后可能由于各個(gè)參數(shù)之間的操作符優(yōu)先級(jí)高于單個(gè)參數(shù)內(nèi)部各部分之間相互作用的操作符優(yōu)先級(jí),如果我們不用括號(hào)保護(hù)各個(gè)宏參數(shù),可能會(huì)產(chǎn)生預(yù)想不 到的情形。比如:

            #define ceil_div(x, y) (x + y - 1) / y

            那么

            a = ceil_div( b & c, sizeof(int) );

            將被轉(zhuǎn)化為:

            a = ( b & c + sizeof(int) - 1) / sizeof(int);

            // 由于+/-的優(yōu)先級(jí)高于&的優(yōu)先級(jí),那么上面式子等同于:

            a = ( b & (c + sizeof(int) - 1)) / sizeof(int);

            這顯然不是調(diào)用者的初衷。為了避免這種情況發(fā)生,應(yīng)當(dāng)多寫幾個(gè)括號(hào):

            #define ceil_div(x, y) (((x) + (y) - 1) / (y))

            消除多余的分號(hào)-Semicolon Swallowing

            通常情況下,為了使函數(shù)模樣的宏在表面上看起來(lái)像一個(gè)通常的C語(yǔ)言調(diào)用一樣,通常情況下我們?cè)诤甑暮竺婕由弦粋€(gè)分 號(hào),比如下面的帶參宏:

            MY_MACRO(x);

            但是如果是下面的情況:

            #define MY_MACRO(x) { \

            /* line 1 */ \

            /* line 2 */ \

            /* line 3 */ }



            //...


            if (condition())

            MY_MACRO(a);

            else

            {...}

            這樣會(huì)由于多出的那個(gè)分號(hào)產(chǎn)生編譯錯(cuò)誤。為了避免這種情況出現(xiàn)同時(shí)保持MY_MACRO(x);的這種寫法,我們 需要把宏定義為這種形式:

            #define MY_MACRO(x) do {

            /* line 1 */ \

            /* line 2 */ \

            /* line 3 */ } while(0)

            這樣只要保證總是使用分號(hào),就不會(huì)有任何問(wèn)題。

            Duplication of Side Effects

            這里的Side Effect是指宏在展開(kāi)的時(shí)候?qū)ζ鋮?shù)可能進(jìn)行多次Evaluation(也就是取值),但是如果這個(gè)宏參數(shù)是一個(gè)函數(shù),那么就有可能被調(diào)用多次從而達(dá) 到不一致的結(jié)果,甚至?xí)l(fā)生更嚴(yán)重的錯(cuò)誤。比如:

            #define min(X,Y) ((X) > (Y) ? (Y) : (X))


            //...



            c = min(a,foo(b));

            這 時(shí)foo()函數(shù)就被調(diào)用了兩次。為了解決這個(gè)潛在的問(wèn)題,我們應(yīng)當(dāng)這樣寫min(X,Y)這個(gè)宏:

            #define min(X,Y) ({ \

            typeof (X) x_ = (X); \

            typeof (Y) y_ = (Y); \

            (x_ < y_) ? x_ : y_; })

            ({...})的作用是將內(nèi)部的幾條語(yǔ)句中最后一條的值返回,它也允許在內(nèi)部聲明變量(因?yàn)樗ㄟ^(guò)大括號(hào)組成了一個(gè)局部 Scope)。

            ==

            #define display(name) printf(""#name"") 
            int main() { 
            display(name); 

            運(yùn)行結(jié)果是name,為什么不是"#name"呢? 
            --------------------------------------------------------------- 

            #在這里是字符串化的意思 
            printf(""#name"") 相當(dāng)于 
            printf("" "name" "") 
            --------------------------------------------------------------- 

            The number-sign or "stringizing" operator (#) converts macro parameters (after expansion) to string constants
            --------------------------------------------------------------- 

            printf("" #name "") <1> 
            相當(dāng)于printf("" "name" "") <2> 
            而<2>中的第2,3個(gè)“中間時(shí)空格 等價(jià)于("空+name+空') 
            --------------------------------------------------------------- 

            ## 連接符與# 符 

            ## 連接符號(hào)由兩個(gè)井號(hào)組成,其功能是在帶參數(shù)的宏定義中將兩個(gè)子串(token)聯(lián)接起來(lái),從而形成一個(gè)新的子串。但它不可以是第一個(gè)或者最后一個(gè)子串。所 謂的子串 (token)就是指編譯器能夠識(shí)別的最小語(yǔ)法單元。具體的定義在編譯原理里有詳盡的解釋,但不知道也無(wú)所謂。同時(shí)值得注意的是#符是把傳遞過(guò)來(lái)的參數(shù)當(dāng) 成字符串進(jìn)行替代。下面來(lái)看看它們是怎樣工作的。這是MSDN上的一個(gè)例子。 

            假設(shè)程序中已經(jīng)定義了這樣一個(gè)帶參數(shù)的宏: 
            #define paster( n ) printf( "token" #n " = %d", token##n ) 

            同時(shí)又定義了一個(gè)整形變 量: 
            int token9 = 9; 

            現(xiàn)在在主程序中以下面的方式調(diào)用這個(gè)宏: 
            paster( 9 ); 

            那 么在編譯時(shí),上面的這句話被擴(kuò)展為: 
            printf( "token" "9" " = %d", token9 ); 

            注意到 在這個(gè)例子中,paster(9);中的這個(gè)”9”被原封不動(dòng)的當(dāng)成了一個(gè)字符串,與”token”連接在了一起,從而成為了token9。而#n也 被”9”所替代。 

            可想而知,上面程序運(yùn)行的結(jié)果就是在屏幕上打印出token9=9 
            --------------------------------------------------------------- 

            #define display(name) printf(""#name"") 
            int main() { 
            display(name); 

            ==================================== 
            特殊性就在 于它是個(gè)宏,宏里面處理#號(hào)就如LS所說(shuō)! 
            處理后就是一個(gè)附加的字符串! 

            但printf(""#name"") ;就不行了! 
            --------------------------------------------------------------- 

            #define display(name) printf(""#name"") 

            該定義 字符串化name, 
            得 到結(jié)果其實(shí)就是 printf("name") 
            (前后的空字符串拿掉) 

            這樣輸出來(lái)的自然是 name 

            從另 外一個(gè)角度講, 
            #是一個(gè)連接符號(hào), 
            參與運(yùn)算了, 自然不會(huì)輸出了 ...

            posted on 2011-01-18 16:58 myjfm 閱讀(2361) 評(píng)論(0)  編輯 收藏 引用 所屬分類: c/c++基礎(chǔ)
            久久精品国产一区二区| 国产精品99久久久精品无码| 久久久久国色AV免费看图片| 91久久国产视频| 国产精品女同久久久久电影院| 久久人与动人物a级毛片| 久久亚洲欧洲国产综合| 亚洲精品美女久久久久99小说| 久久久久亚洲精品天堂久久久久久| 久久中文字幕一区二区| 99久久精品免费看国产一区二区三区| 久久r热这里有精品视频| 亚洲欧美日韩精品久久| 国产综合免费精品久久久| 精品久久久久久无码人妻蜜桃| 久久免费视频一区| 99精品国产免费久久久久久下载| 久久久亚洲AV波多野结衣 | 久久精品综合网| 色综合久久久久无码专区| a级成人毛片久久| 久久久久这里只有精品| 久久天天躁夜夜躁狠狠| 久久99国产综合精品女同| 99久久免费国产精品| 伊人久久大香线蕉综合Av | 亚洲中文字幕久久精品无码喷水| 亚洲va中文字幕无码久久| 久久精品中文騷妇女内射| 国内精品久久久久影院优| 性欧美丰满熟妇XXXX性久久久| 久久人妻少妇嫩草AV无码专区| 国产精品99精品久久免费| 亚洲?V乱码久久精品蜜桃 | 久久青青草原亚洲av无码app | 久久se这里只有精品| 97久久国产露脸精品国产| 国产综合精品久久亚洲| 国产成人精品久久免费动漫| 少妇久久久久久被弄到高潮| 国产∨亚洲V天堂无码久久久|