宏:分为带参数的宏和不带参数的宏;
宏定义以#define关键字后面出现的第一个连续字符序列作为宏名,剩下的部分作为宏体;
宏定义具有文件作用域;
宏的特点和注意事项:
(1)宏定义不是C++/C语句,因此不需要使用语句结束符“;”,否则它也被看做宏体的一部分;
(2)任何宏在编译预处理阶段都只是进行简单的文本替换,不做类型检查和语法检查,这个工作留给编译器进行。参数替换发生在宏扩展之前;
(3)宏定义可以嵌套;
(4)宏不可以调试,因为宏不会进入符号表(符号表是编译器创建的,在编译时宏已经消失了),即使宏替换后出现了语法错误,编译器也会将错误定位到源程序中,而不是定位到具体的某个宏定义中;
(5)程序里使用双引号括起来的字符串中,即使出现了与宏同名的子串,预处理过程也不进行替换;
(6)定义带参数的宏时,宏名和左括号之间不能出现空格,否则使用时会出现问题,但是编译器不会检查出这种错误。
(7)带参数的宏体和各个形参应该分别用括号括起来,以免造成意想不到的错误。举例如下:
#define SQUARE(x)((x)*(x))
如果写成:
#define SQRARE(x)x*x
则:a = SQRARE(3+5);
将被扩展为:a = 3+5*3+5;
(8)不要在引用宏定义的参数列表中使用增量和减量运算符,否则将导致变量多次求值。
(9)带参数的定义不是函数,因此没有函数调用的开销,但是其每一次扩展都会生成重复的代码,结果使可执行代码的体积增大。
(10)inline 函数不可能完全取代宏,各有各的好处。用宏来构造一些重复的、数据和函数混合的、功能较特殊的代码段的时候,其优点就显示出来了。
(11)当我们不再使用某一个宏时,可以使用#undef来取消其定义。例如:
#undef TEXT
简单的删除宏定义会带来许多编译错误。
建议:
(1)宏名采用大写字符组成的单词或其缩写序列,并在各单词之间使用“_”分割;
(2)如果需要公布某个宏,那么该宏定义应当放置在头文件中,否则放置在实现文件(.cpp)的顶部;
(3)不要使用宏来定义新类型名,应该使用typedef,否则容易造成错误;
(4)给宏添加注释时请使用块注释(/* */),而不要使用行注释。因为有些编译器可能会把宏后面的行注释理解为宏体的一部分;
(5)尽量使用const取代宏来定义符号常量;
(6)对于较长的使用频率较高的重复代码片段,建议使用函数或模板而不要使用带参数的宏定义;而对于较短的重复代码片段,可以使用带参数的宏定义,这不仅是出去 类型安全的考虑,而且也是优化与折中的体现;
(7)尽量避免在局部范围内(如函数内、类型定义内等)定义宏,除非它只在该局部范围内使用,否则会损害程序的清晰性。