C語言中的宏定義 #define

整理自網上資料

宏展開的時機:

C語言中如何使用宏C(和C++)中的宏(Macro)屬於編譯器預處理的範疇,屬於編譯期概念(而非運行期概念)
其中預處理器產生編譯器的輸入,它實現以下的功能: 
(1)    文件包含 
可以把源程序中的#include 擴展爲文件正文,即把包含的.h文件找到並展開到#include 所在處。 
(2)    條件編譯 
預處理器根據#if和#ifdef等編譯命令及其後的條件,將源程序中的某部分包含進來或排除在外,通常把排除在外的語句轉換成空行。 

(3)    佈局控制 
#pragma,主要功能是爲編譯程序提供非常規的控制流信息。

(4)    宏替換 
預處理器將源程序文件中出現的對宏的引用展開成相應的宏 定義,即本文所說的#define的功能,由預處理器來完成。 
經過預處理器處理的源程序與之前的源程序有所有不同,在這個階段所進行的工作只是純粹的替換與展開,沒有任何計算功能,所以在學習#define命令時只要能真正理解這一點,這樣纔不會對此命令引起誤解並誤用。 


宏定義中的#和##

(1)在C語言的宏中,#的功能是將其後面的宏參數進行字符串化操作(Stringfication),簡單說就是在對它所引用的宏變量 通過替換後在其左右各加上一個雙引號。

(2)而##被稱爲連接符(concatenator),用來將兩個Token連接爲一個Token。注意這裏連接的對象是Token就行,而不一定 是宏的變量。


宏定義中常見的陷阱

 (0)相同的宏定義會被後面新定義的所替換掉。前面那個宏定義的作用範圍到後面新的宏定義處結束。
(1)未加(),在宏替換後,因爲操作符優先級等原因而出現問題。如:MUL(2+2,2+3)就很有問題
(2)改變了原來的語句塊邏輯(如if後面若不加{}則其作用域爲後面緊跟的第一行),或因爲分號等原因產生的錯誤。
對於替換後爲多行的建議寫成如下的形式,在使用的使用的時候後面加上分號--MACRO(arg1);
#define MACRO(arg1) do { / 
stmt1;   / 
stmt2;   / 
/* ... */  / 
} while(0) 
(3)一個表達式有多次展開,導致出現隱蔽的問題。如MUL(i++,i++)

宏定義的使用技巧

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 */


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(xxx)  ((byte) ((word)(xxx) & 255))

#define  WORD_HI(xxx)  ((byte) ((word)(xxx) >> 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)))

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 _

可以定義宏,例如:

當定義了_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++;



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章