《C++ 宏定义说明(详解).doc》由会员分享,可在线阅读,更多相关《C++ 宏定义说明(详解).doc(6页珍藏版)》请在金锄头文库上搜索。
1、宏定义有无参数宏定义和带参数宏定义两种。无参数的宏定义的一般形式为 # define 标识符 字符序列其中# define之后的标识符称为宏定义名(简称宏名),要求宏名与字符序列之间用空格符分隔。这种宏定义要求编译预处理程序将源程序中随后所有的定名的出现(注释与字符串常量中的除外)均用字符序列替换之。前面经常使用的定义符号常量是宏定义的最简单应用。如有: # define TRUE 1 # define FALSE 0则在定义它们的源程序文件中,凡定义之后出现的单词TRUE将用1替代之;出现单词FALSE将用0替代之。 在宏定义的之前可以有若干个空格、制表符,但不允许有其它字符。宏定义在源程序
2、中单独另起一行,换行符是宏定义的结束标志。如果一个宏定义太长,一行不 够时,可采用续行的方法。续行是在键人回车符之前先键入符号。注意回车要紧接在符号之后,中间不能插入其它符号。 宏定义的有效范围称为宏定义名的辖域,辖域从宏定义的定义结束处开始到其所在的源程序文件末尾。宏定义名的辖域不受分程序结构的影响。可以用预处理命令#undef终止宏定义名的辖域。 在新的宏定义中,可以使用前面已定义的宏名。例如, # define R 2.5 # define PI 3.1415926 # define Circle 2*PI*R # define Area PI* R * R程序中的Circle被展开为2
3、*3.1415926* 2.5, Area被展开为3.1415926*2.5*2.5。 如有必要,宏名可被重复定义。被重复定义后,宏名原先的意义被新意义所代替。 通常,无参数的宏定义多用于定义常量。程序中统一用宏名表示常量值,便于程序前后统一,不易出错,也便于修改,能提高程序的可读性和可移植性。特别是给数组元素个数一个宏定义,并用宏名定义数组元素个数能部分弥补数组元素个数固定的不足。 注意:预处理程序在处理宏定义时,只作字符序列的替换工作,不作任何语法的检查。如果宏定义不当,错误要到预处理之后的编译阶段才能发现。宏定义以换行结束,不需要分号等符号作分隔符。如有以下定定义: # define P
4、I 3.1415926;原希望用PI求圆的周长的语句 c2*PI*r;经宏展开后,变成 c2*3.1415926*r;这就不能达到希望的要求。 带参数宏定义进一步扩充了无参数宏定义的能力,在字符序列替换同时还能进行参数替换。带参数定定义的一般形式为 # define 标识符(参数表)字符序列其中参数表中的参数之间用逗号分隔,字符序列中应包含参数表中的参数。在定义带参数的宏时,宏名标识符与左圆括号之间不允许有空白符,应紧接在一起,否则变成了无参数的宏定义。如有宏定义: # define MAX(A,B) (A) (B)?(A):(B)则代码 y MAX( pq, u+v)将被替换成 y(pq)
5、(uv)?(pq):(uv)。 程序中的宏调用是这样被替换展开的,分别用宏调用中的实在参数字符序列(如pq和uV) 替换宏定义字符序列中对应所有出现的形式参数(如用pq替代所有形式参数A,用uV替代所有形式参数B),而宏定义字符序列中的不是形式参数的其它字 符则保留。这样形成的字符序列,即为宏调用的展开替换结果。宏调用提供的实在参数个数必须与宏定义中的形式参数个数相同。 注意:宏调用与函数调用的区别。函数调用在程序运行时实行,而宏展开是在编译的预处理阶段进行;函数调用占用程序运行时间,宏调用只占编译时间;函数调用 对实参有类型要求,而宏调用实在参数与宏定义形式参数之间没有类型的概念,只有字符序
6、列的对应关系。函数调用可返回一个值,宏调用获得希望的C代码。另 外,函数调用时,实参表达式分别独立求值在前,执行函数体在后。宏调用是实在参数字符序列替换形式参数。替换后,实在参数字符序列就与相邻的字符自然连 接,实在参数的独立性就不一定依旧存在。如下面的宏定义: # define SQR(x) x*x希望实现表达式的平方计算。对于宏调用 PSQR(y)能得到希望的宏展开p y*y。但对于宏调用qSQR(uv)得到的宏展开是quV*uV。显然,后者的展开结果不是程序设计者所希望的。为能保持实在参数替换后的独立性,应在宏定义中给形式参数加上括号。进一步,为了保证宏调用的独立性,作为算式的宏定义也应
7、加括号。如 SQR宏定义改写成: # define SQR(x)*(x)才是正确的宏定义。 对于简短的表达式计算函数,或为了提高程序的执行效率、避免函数调用时的分配存储单元、保留现场、参数值传递、释放存储单元等工作。可将函数定义改写成宏定义。所以合理使用宏定义,可以使程序更简洁。使用一些宏跟踪调试 A N S I标准说明了五个预定义的宏名。它们是: _ L I N E _ (两个下划线),对应%d_ F I L E _ 对应%s_ D A T E _ 对应%s_ T I M E _ 对应%s_ S T D C _ 如果编译不是标准的,则可能仅支持以上宏名中的几个,或根本不支持。记住编译程序 也
8、许还提供其它预定义的宏名。 _ L I N E _及_ F I L E _宏指令在有关# l i n e的部分中已讨论,这里讨论其余的宏名。 _ D AT E _宏指令含有形式为月/日/年的串,表示源文件被翻译到代码时的日期。 源代码翻译到目标代码的时间作为串包含在_ T I M E _中。串形式为时:分:秒。 如果实现是标准的,则宏_ S T D C _含有十进制常量1。如果它含有任何其它数,则实现是 非标准的。 可以定义宏,例如: 当定义了_DEBUG,输出数据信息和所在文件所在行 #ifdef _DEBUG #define DEBUGMSG(msg,date) printf(msg);p
9、rintf(“%d%d%s”,date,_LINE_,_FILE_) #else #define DEBUGMSG(msg,date) #endif 20,宏定义防止使用是错误 用小括号包含。 例如:#define ADD(a,b) (a+b) 用dowhile(0)语句包含多语句防止错误 例如:#difne DO(a,b) a+b; a+; 应用时:if(.) DO(a,b); /产生错误 else 解决方法: #difne DO(a,b) doa+b; a+;while(0) 宏中#和#的用法 一、一般用法 我们使用#把宏参数变为一个字符串,用#把两个宏参数贴合在一起(这里说的是在预处理是
10、对源文件的操作). 用法: #include #include using namespace std;#define STR(s) #s #define CONS(a,b) int(a#e#b)int main() printf(STR(vck); / 输出字符串vck printf(%dn, CONS(2,3); / 2e3 输出:2000 return 0; 二、当宏参数是另一个宏的时候 需要注意的是凡宏定义里有用#或#的地方宏参数是不会再展开.1, 非#和#的情况 #define TOW (2) #define MUL(a,b) (a*b)printf(%d*%d=%dn, TOW,
11、TOW, MUL(TOW,TOW); 这行的宏会被展开为: printf(%d*%d=%dn, (2), (2), (2)*(2); MUL里的参数TOW会被展开为(2).2, 当有#或#的时候 #define A (2) #define STR(s) #s #define CONS(a,b) int(a#e#b)printf(int max: %sn, STR(INT_MAX); / INT_MAX #include 这行会被展开为: printf(int max: %sn, INT_MAX);printf(%sn, CONS(A, A); / compile error 这一行则是: pr
12、intf(%sn, int(AeA);INT_MAX和A都不会再被展开, 然而解决这个问题的方法很简单. 加多一层中间转换宏. 加这层宏的用意是把所有宏的参数在中间层里全部展开, 那么在转换宏里的那一个宏(_STR)就能得到正确的宏参数.#define A (2) #define _STR(s) #s #define STR(s) _STR(s) / 转换宏 #define _CONS(a,b) int(a#e#b) #define CONS(a,b) _CONS(a,b) / 转换宏printf(int max: %sn, STR(INT_MAX); / INT_MAX,int型的最大值,为
13、一个变量 #include 输出为: int max: 0x7fffffff STR(INT_MAX) - _STR(0x7fffffff) 然后再转换成字符串;printf(%dn, CONS(A, A); 输出为:200 CONS(A, A) - _CONS(2), (2) - int(2)e(2)三、#和#的一些应用特例 1、合并匿名变量名 #define _ANONYMOUS1(type, var, line) type var#line #define _ANONYMOUS0(type, line) _ANONYMOUS1(type, _anonymous, line) #define ANONYMOUS(type) _ANONYMOUS0(type, _LINE_) 例:ANONYMOUS(static int); 即: static int _anonymous70; 70表示该行行号; 第一层:ANONYMOUS(static int); - _ANONYMOUS0(static int, _LINE_); 第二层: - _ANONYMOUS1(static int, _anonymous, 70); 第三层: - static int _anonymous70; 即每次只能解开当前层的宏,所以_