1.常规用法
1: #define PI 3.1415926
2: #define CHAR_A 'a'
3: #define STRING_A "a"
2.使用参数
普通用法
1: #define SQUARE(x) ((x)*(x))
2:
3: int a = SQUARE(2);
4: int b = SQUARE(2+3);
使用参数的宏定义中,注意在宏的替换主体部分对参数使用括号。上例中如果是#define SQUARE(x) (x*x),在SQUARE(2+3),就会被展开为(2+3*2+3),结果就大不一样了。
利用宏参数创建字符串:#运算符
1: #define PSQR(X) printf("The square of X is %d./n", ((X)*(X)))
如果使用PSQR(8),输出效果如下:
The square of X is 64.
字符串中的X被看作是普通文本,不做处理。
在类函数的替换部分中,#符号用作一个预处理运算符,它可以把与语言符号转化为字符串。例如,如果x是一个宏参量,那么#x可以把参数名转化为相应的字符串。该过程成为字符串化(stringizing)。
如果想打印出变量的名称,就应该按照下面的方式使用(#运算符)。
1: #define PSQR(X) printf("The square of " #X " is %d./n", ((X)*(X)))
上面PSQR(8)输出的结果就是:
The square of 8 is 64.
预处理的粘合剂:##运算符
和#运算符一样,##运算符可以用于类函数宏的替换部分。另外,##还可以用于类对象宏的替换部分。这个运算符把两个语言符号组合成单个语言符号。例如,可以定义如下的宏:
#define XNAME(n) x##n
这样,下面的宏调用:
XNAME(4)
会展开成下列形式:
x4
比如可以使用:
int XNAME(4) = 14 ;
这展开的意义为:
int x4 = 14 ;
可以使用这种方法来按照某种规则定义变量,只需要将变量规则中可变部分让用户来输入,其余不变部分就通过宏来统一实现。
这里需要注意的几个问题:
如果宏定义是:
#define X_DEF(n) xn
使用X_DEF(4),展开之后的效果是:
xn
这里面的n不会看做是单个语言符号。
如果有这样的定义:
1: #define DEF_1(n) var.##n
2: #define DEF_2(n) var.n
3:
4: #define DEF_3(n) (##n)
5: #define DEF_4(n) n
6: #define DEF_5(n) #n
7:
8: struct __y_t {
9: int a ;
10: }var;
11:
12: int a = 0 ;
13:
14: int main(void)
15: {
16: DEF_1(a) = 5 ; // 语句1,error
17: DEF_2(a) = 5 ; // 语句2,ok
18:
19: DEF_3(a) = 6 ; // 语句3,error
20: DEF_4(a) = 7 ; // 语句4,ok
21:
22: DEF_5(a) = 8 ; // 语句5,error,"n" = 8 ;
23: }
上面代码中:
第4行定义使用括号的原因在于##不能放在顶换部分的行首或者行尾。
语句1和语句3的错误在于,编译器试图将"."与"("与a相连接,但是由于这两个字符都不是有效的C语言命名规则中的字符,所以会提示错误。从这里可以看出,##主要是用来连接成单个语言符号的,所以其前面的字符应该是有效字符。所以x##n正确,但是var.##n反而是错误的。如果前面不是有效字符的话,直接使用常规用法就行了,就像上面代码中的DEF_2一样。
语句5的错误在于展开后相当于
"a" = 8 ;
a被转换为字符串了。
通过上面测试,可以加深对宏定义的理解。
可变宏:…和__VA_ARGS__
宏定义中参数列表的最后一个参数为省略号(三个英文句号,省略号只能代替最后参数)。这样,预定义宏__VA_ARGS__就可以被用在替换部分中,以表明省略号代码什么。例如:
1: #define PR(...) printf(__VA_ARGS__)
2: #define PX(x,...) printf("Message " #x ": " __VA_ARGS__)