C语言中宏的用法总结

来源:互联网 发布:php简历模板 编辑:程序博客网 时间:2024/04/28 23:14

        宏在C语言中使用广泛,但考虑不周全经常会遇到各种陷阱,所以总结了一下。代码均尚未测试。

参考:

http://blog.csdn.net/sunlylorn/article/details/7210344

http://blog.csdn.net/sunlylorn/article/details/7210344

http://blog.chinaunix.net/uid-22566367-id-381995.html

http://blog.csdn.net/zhangxinrun/article/details/5808788

http://blog.csdn.net/tankai19880619/article/details/12015305

http://blog.csdn.net/dj0379/article/details/7558573


规则

1.1 定义

        宏定义的形式如下:

#define name stuff

        其中name与变量名的命名方式相同,替换文本stuff可以是任意字符串。通常情况下,#define指令占一行,替换文本是#define指令行尾部的所有剩余部分内容,但也可以把一个较长的宏定义分成若干行,在每一个待续的行末尾加上一个反斜杠\。宏定义的作用域从其定义点开始,到被编译的源文件的末尾处结束,宏定义可以引用前面出现的宏定义。

        替换只对记号进行,对括在引号中的字符串不起作用。

1.2 取消定义

        可以通过#undef指令取消名字的宏定义。例如,标准库中getchar()一般都是用宏实现。下面的语句取消了标准库里面getchar()的定义,后续调用getchar()的时候,调用的为本地定义的getchar():

#undef getcharint getchar(void) { ... }

        这个在VC和GCC上面没有尝试成功,看起来针对标准中的库函数编译器并不允许这么做。

1.3 带参数的宏

       宏定义可以带参数,形式如下:

#define name(parameter-list) stuff

        其中,parameter-list是一个由逗号分隔的符号列表,可能出现在stuff中。参数列表的左括号必须与name紧邻,二者之间如果有任何空白,参数列表就会被解释为stuff的一部分。一个例子如下:

#define max(A, B) ((A) > (B) ? (A) : (B))

        语句

x = max(p + q, r + s);

        将被展开为下面的形式:

x = ((p + q) > (r + s) ? (p + q) : (r + s));

         从上面的操作可以看出来,宏展开并不关心数据的类型,max宏可以用于任意数据类型。

1.4 #参数

        如果在替换文本中,参数名以#作为前缀,则结果将被扩展为由实际参数替换该参数的带引号的字符串。例如,可以将它与字符串连接运算结合起来编写一个调试打印宏:

#define dprint(expr) printf(#expr " = %g\n", expr)

        下面的语句

dprint(x / y);

        将被扩展为

printf("x / y" " = %g\n", x / y);

        也就是

printf("x / y = %g\n", x / y);

       替换的时候,实际参数中的每个双引号”将被替换为\”,反斜杠\将被替换为\\,因此即使参数里面有格式化字符,替换后的字符串仍然是期望的字符串。

1.5 ##参数

        ##是一个预处理运算符,它为宏提供了一种连接实际参数的手段。如果替换文本中的参数与##相邻,则该参数将被实际参数替换,##与前后的空白字符将被删除,并对替换后的结果重新扫描。简单滴说,它将两个符合组成了单个语言符号。

        例如,下面的宏用于连接两个参数:

#define paste(front, back) front ## back

        paste(name, 1)的结果将建立记号name1

1.6 可变参数

        宏定义中参数列表的最后一个参数为省略号(也就是三个点),预定义的可变参数宏__VA_ARGS__就可以被用在替换部分中,替换省略号所代表的字符串。

#define DBG(...) printf(__VA_ARGS__)DBG("hello, world!\n");

        将被展开为

printf("hello, world!\n");


宏的常用法

2.1 得到指定地址上的一个字节或者字

#define MEM_B(x)  (*((int8 *)(x)))#define MEM_W(x)  (*((uint16 *)(x)))


2.2 得到结构体中指定域的偏移量

#define FPOS(type, field) ((int32)&((type *)0)->field)

        这个方案会有warning出现。

2.3 得到结构体中某域所占用的字节数

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


2.4 按照LSB模式把两个字节转化为一个16位的无符号数

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


2.5 按照LSB模式把一个16位的无符号数转化为两个字节

#define FLOPW(ray, val)\(ray)[0] = ((val) / 256);\(ray)[1] = ((val) & 0xFF)


2.6 得到一个变量的地址

#define B_PTR(var)  ((int8 *)(void *)&(var))#define W_PTR(var)  ((uint16 *)(void *)&(var))


2.7 得到一个16位无符号数的高8位和低8

#define WORD_LO(xxx)  ((uint8)((uint16)(xxx)& 255))#define WORD_HI(xxx)  ((uint8)((uint16)(xxx) >> 8))


2.8 得到一个大于等于x的最小的8的倍数

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


2.9 将一个字母转换为大写

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


2.10 防止溢出的方法

        这个的原理暂时不太清楚,确切地说,不知道它要干嘛。

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


2.11 判断是否十进制的数字

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


2.12 判断是否十六进制的字符

#define HEXCHK(c) (((c) >= '0' && (c) <= '9') || \((c) >= 'A' && (c) <= 'F') || \((c) >= 'a' && (c) <= 'f'))


2.13 得到数组元素的个数

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


2.14 直接在某个地址写入数值

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


2.15 返回一个无符号数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))


2.16 标准中的宏

        ANSI标准说明了五个预定义的宏名。它们是_LINE_,_FILE_,_DATE_,_TIME_,_STDC_。如果编译不是标准的则可能仅支持以上宏名中的几个,或根本不支持。 
_LINE_:标识当前代码所在的行号
_FILE_:标识代码所在的文件名
_DATE_:含有形式为月//年的串,表示源文件被翻译到代码时的日期,如Jan 31 2015
_TIME_:源代码翻译到目标代码的时间字符串,串形式为时:分:秒。 
_STDC_:如果实现是标准的(ANSI C),则宏含有十进制常量1。如果它含有任何其它数,则实现是非标准的。 

可以这样使用这些宏:

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

2.17 宏定义防止使用是错误用小括号包含

    例如:

#define ADD(a,b) (a+b)   

do{}while(0)语句包含多语句防止错误 
例如:

#difne DO(a,b) a+b; a++;   

应用时:

if(...)   

DO(a,b); //产生错误   

else   

...  

解决方法

#define DO(a,b) do{a+b;a++;}while(0)   

0 0