• <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>
            隨筆-4  評論-40  文章-117  trackbacks-0

             

            轉載

            C語言中如何使用宏C(和C++)中的宏(Macro)屬于編譯器預處理的范疇,屬于編譯期概念(而非運行期概念)。下面對常遇到的宏的使用問題做了簡單總結。

            關于#和##

            在C語言的宏中,#的功能是將其后面的宏參數進行字符串化操作(Stringfication),簡單說就是在對它所引用的宏變量通過替換后在其左右各加上一個雙引號。比如下面代碼中的宏:
            #define WARN_IF(EXP)     \
                 do{ if (EXP)     \
                         fprintf(stderr, "Warning: " #EXP "\n"); }    \
                 while(0)
            
            那么實際使用中會出現下面所示的替換過程:
            WARN_IF (divider == 0);
            被替換為
            do {
                 if (divider == 0)
            fprintf(stderr, "Warning" "divider == 0" "\n");
            } while(0);
            
            這樣每次divider(除數)為0的時候便會在標準錯誤流上輸出一個提示信息。

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

            struct command
            {
            char * name;
            void (*function) (void);
            };
            #define COMMAND(NAME) { NAME, NAME ## _command }
            // 然后你就用一些預先定義好的命令來方便的初始化一個command結構的數組了:
            struct command commands[] = {
            COMMAND(quit),
            COMMAND(help),
            ...
            }
            
            COMMAND宏在這里充當一個代碼生成器的作用,這樣可以在一定程度上減少代碼密度,間接地也可以減少不留心所造成的錯誤。我們還可以n個##符號連接 n+1個Token,這個特性也是#符號所不具備的。比如:
            #define LINK_MULTIPLE(a,b,c,d) a##_##b##_##c##_##d
            typedef struct _record_type LINK_MULTIPLE(name,company,position,salary);
            // 這里這個語句將展開為:
            //  typedef struct _record_type name_company_position_salary;
            

            關于...的使用

            ...在C宏中稱為Variadic Macro,也就是變參宏。比如:
            #define myprintf(templt,...) fprintf(stderr,templt,__VA_ARGS__)
            // 或者
            #define myprintf(templt,args...) fprintf(stderr,templt,args)
            
            第一個宏中由于沒有對變參起名,我們用默認的宏__VA_ARGS__來替代它。第二個宏中,我們顯式地命名變參為args,那么我們在宏定義中就可以用args來代指變參了。同C語言的stdcall一樣,變參必須作為參數表的最有一項出現。當上面的宏中我們只能提供第一個參數templt時,C標準要求我們必須寫成:
            myprintf(templt,);
            
            的形式。這時的替換過程為:
            myprintf("Error!\n",);
            替換為:
            fprintf(stderr,"Error!\n",);
            
            這是一個語法錯誤,不能正常編譯。這個問題一般有兩個解決方法。首先,GNU CPP提供的解決方法允許上面的宏調用寫成:
            myprintf(templt);
            
            而它將會被通過替換變成:
            fprintf(stderr,"Error!\n",);
            
            很明顯,這里仍然會產生編譯錯誤(非本例的某些情況下不會產生編譯錯誤)。除了這種方式外,c99和GNU CPP都支持下面的宏定義方式:
            #define myprintf(templt, ...) fprintf(stderr,templt, ##__VAR_ARGS__)
            
            這時,##這個連接符號充當的作用就是當__VAR_ARGS__為空的時候,消除前面的那個逗號。那么此時的翻譯過程如下:
            myprintf(templt);
            被轉化為:
            fprintf(stderr,templt);
            
            這樣如果templt合法,將不會產生編譯錯誤。 這里列出了一些宏使用中容易出錯的地方,以及合適的使用方式。

            錯誤的嵌套-Misnesting

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

            由操作符優先級引起的問題-Operator Precedence Problem

            由于宏只是簡單的替換,宏的參數如果是復合結構,那么通過替換之后可能由于各個參數之間的操作符優先級高于單個參數內部各部分之間相互作用的操作符優先級,如果我們不用括號保護各個宏參數,可能會產生預想不到的情形。比如:
            #define ceil_div(x, y) (x + y - 1) / y
            
            那么
            a = ceil_div( b & c, sizeof(int) );
            
            將被轉化為:
            a = ( b & c   + sizeof(int) - 1) / sizeof(int);
            // 由于+/-的優先級高于&的優先級,那么上面式子等同于:
            a = ( b & (c + sizeof(int) - 1)) / sizeof(int);
            
            這顯然不是調用者的初衷。為了避免這種情況發生,應當多寫幾個括號:
            #define ceil_div(x, y) (((x) + (y) - 1) / (y))
            

            消除多余的分號-Semicolon Swallowing

            通常情況下,為了使函數模樣的宏在表面上看起來像一個通常的C語言調用一樣,通常情況下我們在宏的后面加上一個分號,比如下面的帶參宏:
            MY_MACRO(x);
            
            但是如果是下面的情況:
            #define MY_MACRO(x) { \
            /* line 1 */ \
            /* line 2 */ \
            /* line 3 */ }
            //...
            if (condition())
            MY_MACRO(a);
            else
            {...}
            
            這樣會由于多出的那個分號產生編譯錯誤。為了避免這種情況出現同時保持MY_MACRO(x);的這種寫法,我們需要把宏定義為這種形式:
            #define MY_MACRO(x) do {
            /* line 1 */ \
            /* line 2 */ \
            /* line 3 */ } while(0)
            
            這樣只要保證總是使用分號,就不會有任何問題。

            Duplication of Side Effects

            這里的Side Effect是指宏在展開的時候對其參數可能進行多次Evaluation(也就是取值),但是如果這個宏參數是一個函數,那么就有可能被調用多次從而達到不一致的結果,甚至會發生更嚴重的錯誤。比如:
            #define min(X,Y) ((X) > (Y) ? (Y) : (X))
            //...
            c = min(a,foo(b));
            
            這時foo()函數就被調用了兩次。為了解決這個潛在的問題,我們應當這樣寫min(X,Y)這個宏:
            #define min(X,Y) ({ \
            typeof (X) x_ = (X); \
            typeof (Y) y_ = (Y); \
            (x_ < y_) ? x_ : y_; })
            
            ({...})的作用是將內部的幾條語句中最后一條的值返回,它也允許在內部聲明變量(因為它通過大括號組成了一個局部Scope)。
            posted on 2007-11-22 16:39 李陽 閱讀(201) 評論(0)  編輯 收藏 引用 所屬分類: C++
            久久香蕉国产线看观看乱码| 精品一二三区久久aaa片| 国产综合久久久久久鬼色| 91精品国产综合久久久久久| 99久久国语露脸精品国产| 国内精品久久久久久久影视麻豆 | 国产亚洲婷婷香蕉久久精品| 久久精品成人免费观看97| 亚洲日本va中文字幕久久| 久久99精品久久久久久齐齐| 久久精品国产日本波多野结衣| 日韩精品久久久久久| 97视频久久久| 精品无码久久久久久久动漫| 久久精品一本到99热免费| 亚洲国产精品成人AV无码久久综合影院| 亚洲AV日韩AV永久无码久久| 久久精品国产欧美日韩| 久久青青草原亚洲av无码app| 亚洲精品成人久久久| 伊人久久综在合线亚洲2019| 少妇久久久久久被弄高潮| 99久久这里只精品国产免费| 国产69精品久久久久99尤物| 成人综合伊人五月婷久久| 亚洲精品国精品久久99热一| 久久人人爽人人爽人人片AV东京热| 精品无码久久久久久尤物| 国内精品久久久久影院薰衣草| 色综合久久久久综合99| 久久综合亚洲色HEZYO国产| 91久久成人免费| 国产精品亚洲综合专区片高清久久久| 日产精品久久久久久久| 久久久精品人妻一区二区三区蜜桃| 日日狠狠久久偷偷色综合96蜜桃 | 久久亚洲AV成人无码| 久久精品成人影院| 人妻无码精品久久亚瑟影视| 亚洲国产综合久久天堂| 狠狠综合久久AV一区二区三区|