一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

C語言開發(fā)中宏定義技巧

 邢孟哲圖書館 2014-04-21
    寫好C語言,漂亮的宏定義很重要,使用宏定義可以防止出錯(cuò),提高可移植性,可讀性,方便性等等。下面列舉一些成熟軟件中常用得宏定義……

  1.防止一個(gè)頭文件被重復(fù)包含

  #ifndef COMDEF_H

  #define COMDEF_H

  //頭文件內(nèi)容

  #endif

  2.重新定義一些類型,防止由于各種平臺(tái)和編譯器的不同,而產(chǎn)生的類型字節(jié)數(shù)差異,方便移植

  typedef unsigned char boolean;

  typedef unsigned long int uint32;

  typedef unsigned short uint16;

  typedef unsigned char uint8;

  typedef signed long int int32;

  typedef signed short int16;

  typedef signed char int8;

  //下面的不建議使用

  typedef unsigned char byte;

  typedef unsigned short word;

  typedef unsigned long dword;

  typedef unsigned char uint1;

  typedef unsigned short uint2;

  typedef unsigned long uint4;

  typedef signed char int1;

  typedef signed short int2;

  typedef long int int4;

  typedef signed long sint31;

  typedef signed short sint15;

  typedef signed char sint7;

  3.得到指定地址上的一個(gè)字節(jié)或字

  #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.得到一個(gè)field在結(jié)構(gòu)體(struct)中的偏移量

  #define FPOS( type, field ) \

   ( (dword) &(( type *) 0)-> field )

  6.得到一個(gè)結(jié)構(gòu)體中field所占用的字節(jié)數(shù)

  #define FSIZ( type, field ) sizeof( ((type *) 0)->field )

  7.按照LSB格式把兩個(gè)字節(jié)轉(zhuǎn)化為一個(gè)Word

  #define FLIPW( ray ) ( (((word) (ray)[0]) * 256) + (ray)[1] )

  8.按照LSB格式把一個(gè)Word轉(zhuǎn)化為兩個(gè)字節(jié)

  #define FLOPW( ray, val ) \

  (ray)[0] = ((val) / 256); \

  (ray)[1] = ((val) & 0xFF)

  9.得到一個(gè)變量的地址(word寬度)

  #define B_PTR( var ) ( (byte *) (void *) &(var) )

  #define W_PTR( var ) ( (word *) (void *) &(var) )

  10.得到一個(gè)字的高位和低位字節(jié)

  #define WORD_LO(***) ((byte) ((word)(***) & 255))

  #define WORD_HI(***) ((byte) ((word)(***) >> 8))

  11.返回一個(gè)比X大的最接近的8的倍數(shù)

  #define RND8( x ) ((((x) + 7) / 8 ) * 8 )

  12.將一個(gè)字母轉(zhuǎn)換為大寫

  #define UPCASE( c ) ( ((c) >= 'a' && (c) <= 'z') ? ((c) - 0x20) : (c) )

  13.判斷字符是不是10進(jìn)值的數(shù)字

  #define DECCHK( c ) ((c) >= '0' && (c) <= '9')

  14.判斷字符是不是16進(jìn)值的數(shù)字

  #define HEXCHK( c ) ( ((c) >= '0' && (c) <= '9') ||\

  ((c) >= 'A' && (c) <= 'F') ||\

  ((c) >= 'a' && (c) <= 'f') )

  15.防止溢出的一個(gè)方法

  #define INC_SAT( val ) (val = ((val)+1 > (val)) ? (val)+1 : (val))

  16.返回?cái)?shù)組元素的個(gè)數(shù)

  #define ARR_SIZE( a ) ( sizeof( (a) ) / sizeof( (a[0]) ) )

  17.返回一個(gè)無符號(hào)數(shù)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.對(duì)于IO空間映射在存儲(chǔ)空間的結(jié)構(gòu),輸入輸出處理

  #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)))

  19.使用一些宏跟蹤調(diào)試

  A N S I標(biāo)準(zhǔn)說明了五個(gè)預(yù)定義的宏名。它們是:

  _ L I N E _

  _ F I L E _

  _ D A T E _

  _ T I M E _

  _ S T D C _

  如果編譯不是標(biāo)準(zhǔn)的,則可能僅支持以上宏名中的幾個(gè),或根本不支持。記住編譯程序

  也許還提供其它預(yù)定義的宏名。

  _ L I N E _及_ F I L E _宏指令在有關(guān)# l i n e的部分中已討論,這里討論其余的宏名。

  _ D AT E _宏指令含有形式為月/日/年的串,表示源文件被翻譯到代碼時(shí)的日期。

  源代碼翻譯到目標(biāo)代碼的時(shí)間作為串包含在_ T I M E _中。串形式為時(shí):分:秒。

  如果實(shí)現(xiàn)是標(biāo)準(zhǔn)的,則宏_ S T D C _含有十進(jìn)制常量1.如果它含有任何其它數(shù),則實(shí)現(xiàn)是非標(biāo)準(zhǔn)的。

  可以定義宏,例如:

  當(dāng)定義了_DEBUG,輸出數(shù)據(jù)信息和所在文件所在行

  #ifdef _DEBUG

  #define DEBUGMSG(msg,date) printf(msg);printf(“%d%d%d”,date,_LINE_,_FILE_)

  #else

  #define DEBUGMSG(msg,date)

  #endif

  20,宏定義防止使用是錯(cuò)誤

  用小括號(hào)包含。

  例如:#define ADD(a,b) (a+b)

  用do{}while(0)語句包含多語句防止錯(cuò)誤

  例如:#difne DO(a,b) a+b;\

  a++;

  應(yīng)用時(shí):if(…。)

  DO(a,b); //產(chǎn)生錯(cuò)誤

  else

  C語言中如何使用宏

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

  宏使用中的常見的基礎(chǔ)問題

  #符號(hào)和##符號(hào)的使用

  ……符號(hào)的使用

  宏的解釋方法

  我們能碰到的宏的使用

  宏使用中的陷阱

  常見的基礎(chǔ)性問題:

  關(guān)于#和##

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

  #define WARN_IF(EXP) \

  do{ if (EXP) \ fprintf(stderr, "Warning: " #EXP " "); } \

  while(0)

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

  WARN_IF (divider == 0);

  被替換為

  do {

  if (divider == 0)

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

  } while(0);

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

  而##被稱為連接符(concatenator),用來將兩個(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ù)先定義好的命令來方便的初始化一個(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è)語句將展開為:

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

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

  myprintf("Error! ",);

  替換為:

  fprintf(stderr,"Error! ",);

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

  myprintf(templt);

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

  fprintf(stderr,"Error! ",);

  很明顯,這里仍然會(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í)的翻譯過程如下:

  myprintf(templt);

  被轉(zhuǎn)化為:

  fprintf(stderr,templt);

  這樣如果templt合法,將不會(huì)產(chǎn)生編譯錯(cuò)誤。

  宏是如何解釋的

  宏在日常編程中的常見使用

  宏使用中的陷阱

  這里列出了一些宏使用中容易出錯(cuò)的地方,以及合適的使用方式。

  錯(cuò)誤的嵌套-Misnesting

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

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

  由于宏只是簡(jiǎn)單的替換,宏的參數(shù)如果是復(fù)合結(jié)構(gòu),那么通過替換之后可能由于各個(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ù)模樣的宏在表面上看起來像一個(gè)通常的C語言調(diào)用一樣,通常情況下我們?cè)诤甑暮竺婕由弦粋€(gè)分號(hào),比如下面的帶參宏:

  MY_MACRO(x);

  但是如果是下面的情況:

  #define MY_MACRO(x) {

   \\...

   }

  //……

  if (condition())

  MY_MACRO(a);

  else

  {……}

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

  #define MY_MACRO(x) do {

   \\...

   } while(0)

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

  Duplication of Side Effects

  這里的Side Effect是指宏在展開的時(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è)潛在的問題,我們應(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)部的幾條語句中最后一條的值返回,它也允許在內(nèi)部聲明變量(因?yàn)樗ㄟ^大括號(hào)組成了一個(gè)局部Scope)。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    亚洲欧美国产精品一区二区| 亚洲中文在线观看小视频| 国产精品不卡一区二区三区四区 | 中文字幕久热精品视频在线 | 真实国产乱子伦对白视频不卡| 久久国产精品熟女一区二区三区| 久久精视频免费视频观看| 亚洲一区二区精品福利| 久久香蕉综合网精品视频| 97人妻人人揉人人躁人人| 午夜精品福利视频观看| 欧美日韩一级黄片免费观看| 爱草草在线观看免费视频| 欧美日韩亚洲国产av| 亚洲在线观看福利视频| 亚洲一区二区三区四区| 色无极东京热男人的天堂| 91人妻丝袜一区二区三区| 麻豆剧果冻传媒一二三区| 亚洲中文字幕三区四区| 中文字幕亚洲视频一区二区| 国产精品欧美一区两区| 日韩一区二区三区在线日| 蜜桃av人妻精品一区二区三区| 不卡中文字幕在线视频| 不卡视频免费一区二区三区| 国产毛片对白精品看片| 日韩色婷婷综合在线观看| 久久国产青偷人人妻潘金莲| 偷拍偷窥女厕一区二区视频| 国产成人午夜福利片片| 日韩av生活片一区二区三区| 日本av一区二区不卡| 91久久精品国产成人| 国产欧美一区二区色综合| 精品推荐国产麻豆剧传媒| 91亚洲人人在字幕国产| 黄色国产自拍在线观看| 国产91色综合久久高清| 深夜视频在线观看免费你懂 | 国产三级黄片在线免费看|