C語言巨集定義
阿新 • • 發佈:2022-03-01
本人在寫程式的時候,陣列莫名其妙的越界,導致測試時機器“宕機”(就像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];