1. 程式人生 > 其它 >C語言巨集定義

C語言巨集定義

本人在寫程式的時候,陣列莫名其妙的越界,導致測試時機器“宕機”(就像windows CPU %100利用率一樣)。後來找到了原來是巨集定義的問題,大意如下:

#define A 100

#define B A+2

。。。。。。。。。。

程式中使用的是有

array[b%AEEAY_LEN]=i;

因為這裡的B在定義的時候沒加(),導致陣列賦值時越界了。 痛心疾首!於是去網上搜了下網友大蝦的總結,受益匪淺啊,故分享給大家。

C語言巨集定義技巧(常用巨集定義)本文為轉帖,地址http://blog.21ic.com/user1/69/archives/2006/13695.html

寫好C語言,漂亮的巨集定義很重要,使用巨集定義可以防止出錯,提高可移植性,可讀性,方便性等等。下面列舉一些成熟軟體中常用得巨集定義。。。。。。



1,防止一個頭檔案被重複包含



#ifndefCOMDEF_H

#defineCOMDEF_H

//標頭檔案內容

#endif

2,重新定義一些型別,防止由於各種平臺和編譯器的不同,而產生的型別位元組數差異,方便移植。

typedefunsignedcharboolean;/*Booleanvaluetype.*/



typedefunsignedlongintuint32;/*Unsigned32bitvalue*/

typedefunsignedshortuint16;/*Unsigned16bitvalue*/

typedefunsignedcharuint8;/*Unsigned8bitvalue*/



typedefsignedlongintint32;/*Signed32bitvalue*/

typedefsignedshortint16;/*Signed16bitvalue*/

typedefsignedcharint8;/*Signed8bitvalue*/





//下面的不建議使用

typedefunsignedcharbyte;/*Unsigned8bitvaluetype.*/

typedefunsignedshortword;/*Unsinged16bitvaluetype.*/

typedefunsignedlongdword;/*Unsigned32bitvaluetype.*/



typedefunsignedcharuint1;/*Unsigned8bitvaluetype.*/

typedefunsignedshortuint2;/*Unsigned16bitvaluetype.*/

typedefunsignedlonguint4;/*Unsigned32bitvaluetype.*/



typedefsignedcharint1;/*Signed8bitvaluetype.*/

typedefsignedshortint2;/*Signed16bitvaluetype.*/

typedeflongintint4;/*Signed32bitvaluetype.*/



typedefsignedlongsint31;/*Signed32bitvalue*/

typedefsignedshortsint15;/*Signed16bitvalue*/

typedefsignedcharsint7;/*Signed8bitvalue*/



3,得到指定地址上的一個位元組或字


#defineMEM_B(x)(*((byte*)(x)))

#defineMEM_W(x)(*((word*)(x)))

4,求最大值和最小值

#defineMAX(x,y)(((x)>(y))?(x):(y))

#defineMIN(x,y)(((x)<(y))?(x):(y))

5,得到一個field在結構體(struct)中的偏移量

#defineFPOS(type,field)\

/*lint-e545*/((dword)&((type*)0)->field)/*lint+e545*/

6,得到一個結構體中field所佔用的位元組數

#defineFSIZ(type,field)sizeof(((type*)0)->field)

7,按照LSB格式把兩個位元組轉化為一個Word


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

8,按照LSB格式把一個Word轉化為兩個位元組

#defineFLOPW(ray,val)\

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

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

9,得到一個變數的地址(word寬度)

#defineB_PTR(var)((byte*)(void*)&(var))

#defineW_PTR(var)((word*)(void*)&(var))

10,得到一個字的高位和低位位元組

#defineWORD_LO(xxx)((byte)((word)(xxx)&255))

#defineWORD_HI(xxx)((byte)((word)(xxx)>>8))

11,返回一個比X大的最接近的8的倍數

#defineRND8(x)((((x)+7)/8)*8)

12,將一個字母轉換為大寫

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

13,判斷字元是不是10進值的數字

#defineDECCHK(c)((c)>='0'&&(c)<='9')

14,判斷字元是不是16進值的數字

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

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

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

15,防止溢位的一個方法

#defineINC_SAT(val)(val=((val)+1>(val))?(val)+1:(val))

16,返回陣列元素的個數

#defineARR_SIZE(a)(sizeof((a))/sizeof((a[0])))

17,返回一個無符號數n尾的值MOD_BY_POWER_OF_TWO(X,n)=X%(2^n)

#defineMOD_BY_POWER_OF_TWO(val,mod_by)\

((dword)(val)&(dword)((mod_by)-1))

18,對於IO空間對映在儲存空間的結構,輸入輸出處理

#defineinp(port)(*((volatilebyte*)(port)))

#defineinpw(port)(*((volatileword*)(port)))

#defineinpdw(port)(*((volatiledword*)(port)))



#defineoutp(port,val)(*((volatilebyte*)(port))=((byte)(val)))

#defineoutpw(port,val)(*((volatileword*)(port))=((word)(val)))

#defineoutpdw(port,val)(*((volatiledword*)(port))=((dword)(val)))

[2005-9-9新增]

19,使用一些巨集跟蹤除錯

ANSI標準說明了五個預定義的巨集名。它們是:

_LINE_

_FILE_

_DATE_

_TIME_

_STDC_

如果編譯不是標準的,則可能僅支援以上巨集名中的幾個,或根本不支援。記住編譯程式

也許還提供其它預定義的巨集名。

_LINE_及_FILE_巨集指令在有關#line的部分中已討論,這裡討論其餘的巨集名。

_DATE_巨集指令含有形式為月/日/年的串,表示原始檔被翻譯到程式碼時的日期。

原始碼翻譯到目的碼的時間作為串包含在_TIME_中。串形式為時:分:秒。

如果實現是標準的,則巨集_STDC_含有十進位制常量1。如果它含有任何其它數,則實現是

非標準的。

可以定義巨集,例如:

當定義了_DEBUG,輸出資料資訊和所在檔案所在行

#ifdef_DEBUG

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

#else

#defineDEBUGMSG(msg,date)

#endif



20,巨集定義防止使用是錯誤

用小括號包含。

例如:#defineADD(a,b)(a+b)

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

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

a++;

應用時:if(….)

DO(a,b);//產生錯誤

else



解決方法:#difneDO(a,b)do{a+b;\

a++;}while(0)


巨集中"#"和"##"的用法
一、一般用法
我們使用#把巨集引數變為一個字串,用##把兩個巨集引數貼合在一起.
用法:
#i nclude<cstdio>
#i nclude<climits>
usingnamespacestd;

#defineSTR(s)#s
#defineCONS(a,b)int(a##e##b)

intmain()
{
printf(STR(vck));//輸出字串"vck"
printf("%d\n",CONS(2,3));//2e3輸出:2000
return0;
}

二、當巨集引數是另一個巨集的時候
需要注意的是凡巨集定義裡有用'#'或'##'的地方巨集引數是不會再展開.

1,非'#'和'##'的情況
#defineTOW(2)
#defineMUL(a,b)(a*b)

printf("%d*%d=%d\n",TOW,TOW,MUL(TOW,TOW));
這行的巨集會被展開為:
printf("%d*%d=%d\n",(2),(2),((2)*(2)));
MUL裡的引數TOW會被展開為(2).

2,當有'#'或'##'的時候
#defineA(2)
#defineSTR(s)#s
#defineCONS(a,b)int(a##e##b)

printf("intmax:%s\n",STR(INT_MAX));//INT_MAX#i nclude<climits>
這行會被展開為:
printf("intmax:%s\n","INT_MAX");

printf("%s\n",CONS(A,A));//compileerror
這一行則是:
printf("%s\n",int(AeA));

INT_MAX和A都不會再被展開,然而解決這個問題的方法很簡單.加多一層中間轉換巨集.
加這層巨集的用意是把所有巨集的引數在這層裡全部展開,那麼在轉換巨集裡的那一個巨集(_STR)就能得到正確的巨集引數.

#defineA(2)
#define_STR(s)#s
#defineSTR(s)_STR(s)//轉換巨集
#define_CONS(a,b)int(a##e##b)
#defineCONS(a,b)_CONS(a,b)//轉換巨集

printf("intmax:%s\n",STR(INT_MAX));//INT_MAX,int型的最大值,為一個變數#i nclude<climits>
輸出為:intmax:0x7fffffff
STR(INT_MAX)-->_STR(0x7fffffff)然後再轉換成字串;

printf("%d\n",CONS(A,A));
輸出為:200
CONS(A,A)-->_CONS((2),(2))-->int((2)e(2))

三、'#'和'##'的一些應用特例
1、合併匿名變數名
#define___ANONYMOUS1(type,var,line)typevar##line
#define__ANONYMOUS0(type,line)___ANONYMOUS1(type,_anonymous,line)
#defineANONYMOUS(type)__ANONYMOUS0(type,__LINE__)
例:ANONYMOUS(staticint);即:staticint_anonymous70;70表示該行行號;
第一層:ANONYMOUS(staticint);-->__ANONYMOUS0(staticint,__LINE__);
第二層:-->___ANONYMOUS1(staticint,_anonymous,70);
第三層:-->staticint_anonymous70;
即每次只能解開當前層的巨集,所以__LINE__在第二層才能被解開;

2、填充結構
#defineFILL(a){a,#a}

enumIDD{OPEN,CLOSE};
typedefstructMSG{
IDDid;
constchar*msg;
}MSG;

MSG_msg[]={FILL(OPEN),FILL(CLOSE)};
相當於:
MSG_msg[]={{OPEN,"OPEN"},
{CLOSE,"CLOSE"}};

3、記錄檔名
#define_GET_FILE_NAME(f)#f
#defineGET_FILE_NAME(f)_GET_FILE_NAME(f)
staticcharFILE_NAME[]=GET_FILE_NAME(__FILE__);

4、得到一個數值型別所對應的字串緩衝大小
#define_TYPE_BUF_SIZE(type)sizeof#type
#defineTYPE_BUF_SIZE(type)_TYPE_BUF_SIZE(type)
charbuf[TYPE_BUF_SIZE(INT_MAX)];
-->charbuf[_TYPE_BUF_SIZE(0x7fffffff)];
-->charbuf[sizeof"0x7fffffff"];
這裡相當於:
charbuf[11];