预处理详解
预定义符号
printf("file:%s line:%d\n", __FILE__, __LINE__);
printf("data:%s time:%d\n", __DATE__, __TIME__);
#define定义宏
为了不让替换后出现优先级改变的情况,通常在宏定义时会为表达式加上括号。
需要注意的是#define有自己的替换规则:
- 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
- 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
#define中的#和##
int i = 10;
#define PRINT(FORMAT, VALUE)\
printf("the value of " #VALUE "is "FORMAT "\n", VALUE);
PRINT("%d", i+3)
#define ADD_TO_SUM(num, value) \
sum##num += value;
ADD_TO_SUM(5, 10);//作用是:给sum5增加10.
另外需要注意的是:
当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能
出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。
所以在进行宏定义时尽量不使用类似于如下代码:
x++;
x--;
因为会在x原来的值上产生增减。
宏和函数
宏通常被用于进行简单运算
#define MAX(a, b) ((a)>(b)?(a):(b))
不使用函数而使用宏定义实现有两个原因:
- 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。
所以宏比函数在程序的规模和速度方面更胜一筹。 - 更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等可以用于>来比较的类型。
宏是类型无关的。
宏对比函数也有劣势:
- 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序
的长度。 - 宏是没法调试的。
- 宏由于类型无关,也就不够严谨。
- 宏可能会带来运算符优先级的问题,导致程容易出现错。
宏的参数可以出现类型,但是函数不行
一般我们在对宏命名时统一使用大写,函数命名通常使用驼峰法。
#undef
条件编译
调试性的代码,删除可惜,保留又碍事,所以我们可以选择性的编译。
头文件包含
查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件。
如果找不到就提示编译错误。
VS环境的标准头文件路径:
嵌套文件包含
有时会出现源文件多次包含头文件的情况,如果不加修改进行编译,最后产生代码冗余的情况,所以我们在创建头文件后VS通常会自动帮我们补充一行代码来保证没有多次包含同一头文件。
其他还有许多的预处理指令,在这就不多说了。