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.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 宏的常用法
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)
- C语言中宏的用法总结
- C语言中define的用法总结
- 【C语言】宏定义的用法总结
- 关于C语言中return用法的一些总结
- 【转载】C语言中extern的用法总结
- C/C++语言中关于const用法的总结
- C/C++语言中关于const用法的总结
- C语言中const,volatile,restrict的用法总结
- C语言中const,volatile,restrict的用法总结
- C语言宏中"#"和"##"的用法
- C语言宏中"#"和"##"的用法
- C语言中宏的一些用法
- C语言:宏中"#"和"##"的用法
- C语言宏中"#"和"##"的用法
- C语言宏中"#"和"##"的用法
- C语言宏中"#"和"##"的用法
- C语言宏中#、##的用法详解
- C语言中宏的高级用法
- 常用的OpenCV函数速查
- DFS分别用邻接矩阵和邻接表访问
- 安装oracle过程中增加linux的swap
- Android之Handler(主线程,自己线程,其他线程互发消息)
- poj 2991 线段树区间更新
- C语言中宏的用法总结
- 收藏的Android非常好用的组件或者框架
- Android项目使用外部.so
- Spring IOC和DI的理解
- Volley 源码解析
- python学习笔记(十五) - ORM框架(SQLAlchemy)
- 【c++笔记十三】c++中的输入、输出和文件操作
- 小样本分析
- Extremal Region(极值区域)文本定位与识别法-学习笔记(四)