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

            elva

            C語言宏定義使用技巧

            寫好C語言,漂亮的宏定義很重要,使用宏定義可以防止出錯,提高可移植性,可讀性,方便性 等等。下面列舉一些成熟軟件中常用得宏定義。。。。。。
            1,防止一個頭文件被重復包含

            #ifndef COMDEF_H
            #define COMDEF_H
            //頭文件內容
            #endif
            2,重新定義一些類型,防止由于各種平臺和編譯器的不同,而產生的類型字節數差異,方便移植。
            typedef unsigned char boolean; /* Boolean value type. */
            typedef unsigned long int uint32; /* Unsigned 32 bit value */
            typedef unsigned short uint16; /* Unsigned 16 bit value */
            typedef unsigned char uint8; /* Unsigned 8 bit value */
            typedef signed long int int32; /* Signed 32 bit value */
            typedef signed short int16; /* Signed 16 bit value */
            typedef signed char int8; /* Signed 8 bit value */
            //下面的不建議使用
            typedef unsigned char byte; /* Unsigned 8 bit value type. */
            typedef unsigned short word; /* Unsinged 16 bit value type. */
            typedef unsigned long dword; /* Unsigned 32 bit value type. */
            typedef unsigned char uint1; /* Unsigned 8 bit value type. */
            typedef unsigned short uint2; /* Unsigned 16 bit value type. */
            typedef unsigned long uint4; /* Unsigned 32 bit value type. */
            typedef signed char int1; /* Signed 8 bit value type. */
            typedef signed short int2; /* Signed 16 bit value type. */
            typedef long int int4; /* Signed 32 bit value type. */
            typedef signed long sint31; /* Signed 32 bit value */
            typedef signed short sint15; /* Signed 16 bit value */
            typedef signed char sint7; /* Signed 8 bit value */
            3,得到指定地址上的一個字節或字
            #define MEM_B( x ) ( *( (byte *) (x) ) )
            #define MEM_W( x ) ( *( (word *) (x) ) )
            4,求最大值和最小值
            #define MAX( x, y ) ( ((x) > (y)) ? (x) : (y) )
            #define MIN( x, y ) ( ((x) < (y)) ? (x) : (y) )
            5,得到一個field在結構體(struct)中的偏移量
            #define FPOS( type, field ) \
            /*lint -e545 */ ( (dword) &(( type *) 0)-> field ) /*lint +e545 */
            6,得到一個結構體中field所占用的字節數
            #define FSIZ( type, field ) sizeof( ((type *) 0)->field )
            7,按照LSB格式把兩個字節轉化為一個Word
            #define FLIPW( ray ) ( (((word) (ray)[0]) * 256) + (ray)[1] )
            8,按照LSB格式把一個Word轉化為兩個字節
            #define FLOPW( ray, val ) \
            (ray)[0] = ((val) / 256); \
            (ray)[1] = ((val) & 0xFF)
            9,得到一個變量的地址(word寬度)
            #define B_PTR( var ) ( (byte *) (void *) &(var) )
            #define W_PTR( var ) ( (word *) (void *) &(var) )
            10,得到一個字的高位和低位字節
            #define WORD_LO(***) ((byte) ((word)(***) & 255))
            #define WORD_HI(***) ((byte) ((word)(***) >> 8))
            11,返回一個比X大的最接近的8的倍數
            #define RND8( x ) ((((x) + 7) / 8 ) * 8 )
            12,將一個字母轉換為大寫
            #define UPCASE( c ) ( ((c) >= 'a' && (c) <= 'z') ? ((c) - 0x20) : (c) )
            13,判斷字符是不是10進值的數字
            #define DECCHK( c ) ((c) >= '0' && (c) <= '9')
            14,判斷字符是不是16進值的數字
            #define HEXCHK( c ) ( ((c) >= '0' && (c) <= '9') ||\
            ((c) >= 'A' && (c) <= 'F') ||\
            ((c) >= 'a' && (c) <= 'f') )
            15,防止溢出的一個方法
            #define INC_SAT( val ) (val = ((val)+1 > (val)) ? (val)+1 : (val))
            16,返回數組元素的個數
            #define ARR_SIZE( a ) ( sizeof( (a) ) / sizeof( (a[0]) ) )
            17,返回一個無符號數n尾的值MOD_BY_POWER_OF_TWO(X,n)=X%(2^n)
            #define MOD_BY_POWER_OF_TWO( val, mod_by ) \
            ( (dword)(val) & (dword)((mod_by)-1) )
            18,對于IO空間映射在存儲空間的結構,輸入輸出處理
            #define inp(port) (*((volatile byte *) (port)))
            #define inpw(port) (*((volatile word *) (port)))
            #define inpdw(port) (*((volatile dword *)(port)))
            #define outp(port, val) (*((volatile byte *) (port)) = ((byte) (val)))
            #define outpw(port, val) (*((volatile word *) (port)) = ((word) (val)))
            #define outpdw(port, val) (*((volatile dword *) (port)) = ((dword) (val)))
            [2005-9-9添加]
            19,使用一些宏跟蹤調試
            A N S I標準說明了五個預定義的宏名。它們是:
            _ L I N E _
            _ F I L E _
            _ D A T E _
            _ T I M E _
            _ S T D C _
            如果編譯不是標準的,則可能僅支持以上宏名中的幾個,或根本不支持。記住編譯程序
            也許還提供其它預定義的宏名。
            _ L I N E __ F I L E _宏指令在有關# l i n e的部分中已討論,這里討論其余的宏名。
            _ D AT E _宏指令含有形式為月//年的串,表示源文件被翻譯到代碼時的日期。
            源代碼翻譯到目標代碼的時間作為串包含在_ T I M E _中。串形式為時:分:秒。
            如果實現是標準的,則宏_ S T D C _含有十進制常量1。如果它含有任何其它數,則實現是
            非標準的。
            可以定義宏,例如:
            當定義了_DEBUG,輸出數據信息和所在文件所在行
            #ifdef _DEBUG
            #define DEBUGMSG(msg,date) printf(msg);printf(“%d%d%d”,date,_LINE_,_FILE_)
            #else
            #define DEBUGMSG(msg,date)
            #endif
            20,宏定義防止使用是錯誤
            用小括號包含。
            例如:#define ADD(a,b) a+b
            do{}while(0)語句包含多語句防止錯誤
            例如:#difne DO(a,b) a+b;\
            a++;
            應用時:if(.)
            DO(a,b); //產生錯誤
            else


            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 2008-08-11 15:13 葉子 閱讀(2595) 評論(0)  編輯 收藏 引用 所屬分類: C\C++

            国产精品久久久久乳精品爆| 日本久久久精品中文字幕| 久久精品中文字幕有码| 日本精品久久久久久久久免费| 性做久久久久久久久老女人| 久久精品亚洲日本波多野结衣| 精品国产婷婷久久久| 色婷婷综合久久久中文字幕| 久久国产成人| 亚洲国产另类久久久精品小说| 四虎国产永久免费久久| 77777亚洲午夜久久多人| 久久99免费视频| 一本色道久久综合亚洲精品| 久久综合狠狠综合久久激情 | 久久精品日日躁夜夜躁欧美| 一级做a爰片久久毛片16| 久久人人爽人人人人爽AV| 久久精品国产亚洲精品| 久久精品国产精品青草| 亚洲国产精品18久久久久久| 久久国产免费直播| 国内精品久久久久久久久| 久久久噜噜噜久久熟女AA片| 欧美伊人久久大香线蕉综合| 久久久综合香蕉尹人综合网| 狠狠色综合网站久久久久久久| 久久久久成人精品无码中文字幕 | 国产成人香蕉久久久久| 国内精品九九久久久精品| 久久综合久久美利坚合众国| 久久有码中文字幕| 久久久受www免费人成| 国内精品久久久久久麻豆| 久久久国产精品网站| 一本大道久久a久久精品综合| av国内精品久久久久影院| 狠色狠色狠狠色综合久久| 免费精品99久久国产综合精品| 久久久国产精品网站| 久久国产精品久久国产精品|