宏的本质
宏的本质就是具有一定规则的文本替换。
惯例以及好的习惯:
1、
2、
3、
4、
特性:
1、
在一个宏定义中,引用之前已经定义过的另外一个宏是可以的。例如:
#define PI 3.14
#define DOUTLBE_PI (2*PI)
2、
如果不想让某个宏继续存在,则可以使用
3、常用宏
指令 用途
# 空指令,无任何效果
#include 包含一个源代码文件
#define 定义宏
#undef 取消已定义的宏
#if 如果给定条件为真,则编译下面代码
#ifdef 如果宏已经定义,则编译下面代码
#ifndef 如果宏没有定义,则编译下面代码
#elif 如果前面的#if给定条件不为真,当前条件为真,则编译下面代码,其实就是else if的简写
#endif 结束一个#if……#else条件编译块
#error 停止编译并显示错误信息
宏的一般用法
1、
不带参数的宏,有时也被称为“对象模样的宏”。
(1)字面值替换(特定重复使用的值,数组维数的表示等)
#define PI 3.14
这样做的最大好处是维护方便,例如将来为了提高精度,可以将数值改为
(2)
为了防卫重复声明和
# ifndef __DUMMY_H__
#define __DUMMY_H__
//
#endif // __DUMMY_H__
(3)条件编译
条件编译也是最常用的预处理技术之一。理论上讲,条件编译可以不依赖于宏,不过事实是,如果离开宏定义,条件编译也就没有了实际作用,唯 一的作用可能就剩下用
2、
带参数的宏,有时也被称为“函数模样的宏”,宏函数。
例如:#define SUM(a,b) a+b
要注意的问题:
(1)在使用宏函数的时候要特别注意括号的使用:
如上宏函数在此情况下使用时就会出现问题:10/SUM(10,1);该宏函数展开后就变成10/10+1;所以上宏函数应该修改为 #define SUM(a,b) ((a)+(b))
因此在做宏函数时,最好是对每一个参数都加上一个括号
(2)在使用++/-- 运算时会出现问题
如:#define CUBE(X) ((X)*(X)*(X))
当为int num=9;CUBE(num++);
宏函数展开为(num++)*(num++)*(num++)=((9)*(10)*(11));
此时就出现问题了
(3)同时在使用宏函数时,不会对参数的类型进行检查,也会容易出现问题。
当然在使用函数时,函数的实现不止一行,当函数需要多行实现的时候可以使用‘/’续行符来实现多行。
3、#符号的使用
(1)#运算符
出现在宏定义中的#运算符把跟在其后的参数转换成一个字符串。有时把这种用法的#称为字符串化运算符。
#define PASTE(n) "adhfkj"#n
int main()
{
printf("%s\n",PASTE(15));
return 0;
}
//out: adhfj15
宏定义中的#运算符告诉预处理程序,把源代码中任何传递给该宏的参数转换成一个字符串。
(2)##运算符
##运算符用于把参数连接到一起。预处理程序把出现在##两侧的参数合并成一个符号
#define NUM(a,b,c) a##b##c
#define STR(a,b,c) a##b##c
int main()
{
printf("%d\n",NUM(1,2,3));
printf("%s\n",STR("aa","bb","cc"));
return 0;
}
//最后程序的输出为:
123
aabbcc
此处的##的使用,在MFC中的消息机制中使用到过,可以参照那里一起看下。
宏函数诚然存在缺点和弊端但是也有其优点和具体问题的用武之地,比如在MFC的消息映射中.....
我总结宏的使用方法是因为我在做测试算法的运行时间时使用了
如下的宏:
#define COMPARE_TIME(FUNCTION) {\
clock_t t1 = clock();\
FUNCTION;\
clock_t t2 = clock();\
cout << double(t2-t1)/CLOCKS_PER_SEC << endl;}
因为要测试一个排序算法的时间需要记录其开始时间和结束时间,在这两个时间之间夹的时间就渐进于算法的运行时间,如果不想每次都把这几行代码从写一遍,我们首先想到的是写成函数,但是我们会发现由于算法的函数类型并不相同,那么我们就没有办法设定该函数的参数,所以设计函数这个思路并不适用,此时就发现此场合使用宏函数还是很好用的