C语言预处理有三种:宏定义,文件包含,条件编译
文件包含很常见,条件编译则可以通过是#ifdef, #if 使得编译时的源码包含(不包含)某些内容。宏定义则主要是进行替换,本文主要记录宏定义的相关内容。
部分内容参考了贴吧:http://tieba.baidu.com/f?kz=1044315227
##语法
1 |
需要注意的是:
- 名称一般全部大写
- 宏定义是预编译使用的,不是语句,末尾没有分号
- 宏定义可以嵌套
- 可以使用#undef来终止宏定义的作用域
- 不替换””中的字符串内容
##带参宏定义 宏定义可以带参数,用小括号表示参数:
1 |
|
不过这样容易出现一些问题:
1 |
|
预编译只对宏进行简单的替换
###常见用法
- 防止头文件被重复包含
1 |
|
- 得到指定地址上的一个字节/字
1 |
- 求最大值or最小值
1 |
- 得到一个变量的地址
1 |
- ROUNDUP or ROUNDDOWN
1 |
- HIGH/LOW Byte
1 |
- UPCASE
1 |
- 防止溢出
1 |
- 防止出错
1 |
|
struct相关用法
为了得到一个field在结构体中的偏移,可以使用这样宏
1 |
|
由于取地址会将field的偏移加上struct的首地址后返回,而我们如果将0转换为当前struct的首地址的话,那么返回的值刚好就是field在此struct中的偏移了。
为了得到field所占用的字节数:
1 |
##GCC Standard Predefined Macros
GCC预定义了一些宏能帮助我们进行调试和输出,常见的有:__FILE__
, __LINE__
, __DATE__
, __TIME__
, __attributes__
, __DEBUG__
等等,
具体可以参考:
GCC Standard Predefined Macros
GCC Extensions to the C Language Family
##和#的用法
使用#可以将宏参数变成一个字符串,而##则可以把宏参数单纯地拼接起来
1 |
|
##宏的嵌套 当宏里面含有#或##的时候,这一部分的宏不会再被展开
1 |
|
为了解决这种情况,需要加一层中间转换宏:
1 |
|
###相关用法
- 合并匿名变量
1 |
|
- 填充结构
1 |
|
- 得到数值类型所对应的字符串的大小
1 |
##typedef
许多人会把typedef也作为预处理的一部分,包括我参考的这个贴吧的原文。但是typedef与define本质是不同的,typedef是语句。
语句意味着:
1. 末尾有分号
2. 在编译时才进行处理
3. 在函数内部不能使用typedef
4. 编译器会对typedef后变量的使用进行编译时检查
1 |
|