C语言预处理有三种:宏定义,文件包含,条件编译
文件包含很常见,条件编译则可以通过是#ifdef, #if 使得编译时的源码包含(不包含)某些内容。宏定义则主要是进行替换,本文主要记录宏定义的相关内容。
部分内容参考了贴吧:http://tieba.baidu.com/f?kz=1044315227
##语法
1
| #define identifier string
|
需要注意的是:
- 名称一般全部大写
- 宏定义是预编译使用的,不是语句,末尾没有分号
- 宏定义可以嵌套
- 可以使用#undef来终止宏定义的作用域
- 不替换””中的字符串内容
##带参宏定义
宏定义可以带参数,用小括号表示参数:
1 2 3 4 5
| #define S(a,b) a*b
area = S(3,2); => area = a*b; => area = 3*2;
|
不过这样容易出现一些问题:
1 2 3 4 5 6
| #define S(r) r*r area = S(a + b); => area = a + b * a + b;
#define S(r) ((r)*(r)) => area = ((a + b) * (a + b));
|
预编译只对宏进行简单的替换
###常见用法
1 2 3 4
| #ifndef COMDEF_H #define COMDEF_H
#endif
|
1 2
| #define MEM_B(x) (*( (byte *)(x) )) #define MEM_W(x) (*( (word *)(x) ))
|
1
| #define MAX(x,y) ( ((x) > (y)) ? (x) : (y) )
|
1
| #define B_PTR(var) ( (byte *)(void *) &(var) )
|
1 2
| #define ROUNDUP(var, base) (((var) + (base) - 1) / (base) * (base)) #define ROUNDDOWN(var, base) ((var) / (base) * (base))
|
1 2
| #define WORD_LOW(var) ((byte) ((word)(var) & 255)) #define WORD_HIGH(var) ((byte) ((word)(var) >> 8))
|
1
| #define UPCASE (c) ( ((c) >= 'a' && (c) <= 'z') ? ((c) - 0x20) : (c) )
|
1
| #define INC_SAT(var) (var = ((var) + 1 > (var)) ? (var) + 1 : (var))
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #define ADD(a, b) (a + b);\ a++
#define ADDv2(a, b) do {\ a + b;\ a++;\ } while(0)
if (condition) ADD(a, b); => if (condition) (a + b); a++;
if (condition) ADDv2(a, b); => if (condition) do { a + b; a++; } while(0);
|
struct相关用法
为了得到一个field在结构体中的偏移,可以使用这样宏
1 2
| #define FPOS(type, field) \ ( (dword) &((type *)0)->field )
|
由于取地址会将field的偏移加上struct的首地址后返回,而我们如果将0转换为当前struct的首地址的话,那么返回的值刚好就是field在此struct中的偏移了。
为了得到field所占用的字节数:
1
| #define FSIZ(type, field) sizeof( ((type *)0)->field )
|
##GCC Standard Predefined Macros
GCC预定义了一些宏能帮助我们进行调试和输出,常见的有:__FILE__, __LINE__, __DATE__, __TIME__, __attributes__, __DEBUG__等等,
具体可以参考:
GCC Standard Predefined Macros
GCC Extensions to the C Language Family
##和#的用法
使用#可以将宏参数变成一个字符串,而##则可以把宏参数单纯地拼接起来
1 2 3 4 5 6 7 8
| #define STR(s) #s #define CONS(a, b) int(a##e##b)
printf(STR(alice)); => printf("alice");
printf("%d", CONS(2,3)); => printf("%d", 2e3);
|
##宏的嵌套
当宏里面含有#或##的时候,这一部分的宏不会再被展开
1 2 3 4 5 6 7 8 9 10 11 12 13
| #define TOW (2) #define MUL(a, b) (a * b)
printf("%d", MUL(TOW, TOW)); => printf("%d", ((2) * (2)));
#define NUM (2) #define STR(s) #s #define CONS(a, b) int(a##e##b)
STR(NUM) => "NUM" CONS(NUM, NUM) => int(NUMeNUM)
|
为了解决这种情况,需要加一层中间转换宏:
1 2 3 4 5 6 7 8 9 10 11 12
| #define _STR(s) #s #define STR(s) _STR(s) #define CONS1(a, b) axxxxxb #define CONS2(a, b) a##xxxxx##b
printf(STR(CONS2(1, 2))); => printf(_STR(1xxxxx2)); => printf("1xxxxx2");
printf(STR(CONS1(1, 2))); => printf(_STR(axxxxxb)); => printf("axxxxxb");
|
###相关用法
1 2 3 4 5 6 7 8
| #define __ANONYMOUS1(type, var, line) type var##line #define _ANONYMOUS0(type, line) __ANONYMOUS1(type, _anonymous, line) #define ANONYMOUS(type) _ANONYMOUS0(type,__LINE__)
ANONNYMOUS(static int); => _ANONYMOUS0(static int, __LINE__); => __ANONYMOUS1(static int, _anonymous, 33); => static int _anonymous33;
|
1 2 3 4 5 6 7 8 9 10
| #define FILL(a) {a, #a}
enum IDD{OPEN, CLOSE}; struct MSG { IDD id; const char * msg; }
struct MSG _msg[] = { FILL(OPEN), FILL(CLOSE) }; => struct MSG _MSG[] = { { OPEN, "OPEN" }, { CLOSE, "CLOSE" } };
|
1 2
| #define _TYPE_STR_SIZE(type) sizeof #type #define TYPE_STR_SIZE(type) _TYPE_STR_SIZE(type)
|
##typedef
许多人会把typedef也作为预处理的一部分,包括我参考的这个贴吧的原文。但是typedef与define本质是不同的,typedef是语句。
语句意味着:
1. 末尾有分号
2. 在编译时才进行处理
3. 在函数内部不能使用typedef
4. 编译器会对typedef后变量的使用进行编译时检查
1 2 3 4 5 6 7 8 9 10 11 12
| #define PINT int * #typedef int * pint;
const pint p1; => int * const p1; p1 = NULL; *p1 = 0;
const PINT p2; => const int *p2; <=> int const *p2; p2 = NULL; *p2 = 0;
|