#define的用法体会

#define 创建一个宏,该宏是标识符或参数化标识符与标记字符串的关联。 在定义宏之后,编译器可用标记字符串替换源文件中标识符的每个匹配项。
语法 #define identifier token-string
#define identifier ( identifier 1 , ... , identifier n ) token-string
备注 1 #define 指令促使编译器用 token-string 替换源文件中 identifier 的每个匹配项。 仅当 identifier 构成标记时才替换它。 也就是说,如果 identifier 出现在注释、字符串或较长的标识符中,则不会替换它。
2 第二种形式类似于函数,例如
#define mul(a,b) (a*b) 形参名称将出现在 token-string 中以标记实际值的替换位置。
token-string 中前面未带stringizing (#)、charizing(#@) 或 token-pasting (##) 运算符或后面未跟 ## 运算符的每个形参将由对应的实参替换。

注意:
1 宏就是简单的字符替换,注意替换后的运算符优先级可能会影响宏定义所要表达的。所以要善于使用小括号。例如:
#define Add(a,b) a+b如果使用的时候:2*Add(1,2) 原本想让它等于6,但替换后变成2*1+2=4。所以要注意。
2 #define中的换行符是反斜杠\。如果一行没写完,直接加一个\,下一行继续写就可以了。
3 注意这里只是字符替换,宏定义中不应含有return等语句,想一想替换后,会变成什么样子。

宏的返回值的两种形式:
1 操作符。
#define Max(a,b) (((a)>(b))(a):(b))
#define add(a,b) ((a)+(b))
2 将返回值赋值给一个额外的形参,这个形参就是返回值。
#define cal(a,b,c) {c=a+b; }

一个非常有意思的例子:
定义一个求最大值的宏。
可能会这么写:#define Max(a,b) ((a)>(b) ?a):(b))
这么做可能对于大多数情况是没有错误的,但是其实这么做是不严谨的。例如:
#define Max(a,b) ((a)>(b)? (a):(b))
int fun(int *a)
{
*a=*a+1;
return *a;
}
main()
{
int a=1,b=2,c;
c=Max(a,fun(&b));
}//我们想得到的c的值应该是3,但实际会发现c的值是4.
因为首先我们替换:c=((a)>(fun(&b))? (a):(fun(&b)));
可以发现fun执行了两次,b的值加了两次变成了4.所以结果是4.

这里给出一个严谨的做法:
使得参数只被执行一次。
#define Max(a,b) ({\
typeof(a) _a=(a); \
typeof(b) _b=(b); \
(_a>_b)? _x : _y; })
这样就不会有上述的问题了。
注意:({...})的作用是将内部的几条语句中最后一条的值返回,它也允许在内部声明变量(因为它通过大括号组成了一个局部Scope)。用此可以达到return的目的。
!!注:这里使用了typeof,它是c的一个新扩展。而在vs2005,2010,2013中都无法使用。只能在在线编译器下执行,结果是对的。
观察发现:vs中不识别关键字typeof了。在C++中可用decltype实现相似功能,求某个变量的类型。而且vs中也不可以有这样的({...})的东西了。要么#define.. () 要么#define .. {}。
所以上面的做法可以改成:
#define MIN(X,Y,M) {\
decltype(X)x_ = (X); \
decltype(Y)y_ = (Y); \
M=(x_ < y_) ? x_ : y_; }
使用时:int a=1,b=2,c; Max(a,b,c); c为返回值,不参与运算。


#与##与#@在#define中的用法:

#的意思是不展开参数
如果在token-string中出现以#开头,意思是:不展开参数,直接替换。
如果在token-string中出现不以#开头,意思是:展开参数,直接替换。
例如:
#define f(a) a+1
#define T(a) #a
#define H(a) a
T(f(a))->f(a)->a+1
H(f(a))->H(a+1)->a+1
虽然结果一样,但是过程不一样。

##的意思是字符串连接符
例如:
#define f(a,b) a##b##lobe
结果就是ablobe
注意:##不能出现在开头,也不能出现在结尾。只能出现在中间。
【#define的用法体会】
#@字符化运算符 Microsoft 专用
charizing 运算符只能与宏的参数一起使用。 如果宏的定义中的形参前有 #@,则会在扩展宏时用单引号括起实参并将其视为一个字符。

#define makechar(x)#@x

==
char a= makechar(x);
例如:
#define H(x) #@x
cout< 输出结果:a
cout< 输出结果:24930
注解:就是相当于把#@后面的实参变成一个字符型的字符。

    推荐阅读