C++笔记 --- 预编译(宏)
目录
(本章节中例子都是用 VS2005 编译调试的)
宏
不带参数的宏 -----------------------------------------------------
形式:
#define 宏名称 替换主体
注意:
- 替换主体中可以有空格空格不是替换主体的一部分(这也很好理解,因为编程的时候执行语句也一般是忽视中间的空格的就好比 a = 3; 与 a=3;是等价的一样 ),例如 #define SIX 2 *3 (等价于 #define SIX 2*3)
- 但是替换主体的内容还是要遵照一定的规定表达式的数据和操作符中间可以有空格但是一个数据中不能存在 (比如不能这样定义宏 #define PI 3 .14 或者 #define PI 2 2)
例子:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #define PI 3.14 //下文出现 PI 的时候都用 3.14 去替换 2 #define SIX 3+3 //下文出现 SIX 的时候都用 3+3 去替换 3 4 void mian() 5 { 6 int x; 7 x = PI; //等价于 x = 3.14 8 x = SIX * 5 //这里注意了,x的值为 18 而不是 30 9 //因为宏只做简单替换也就是这里等价于 x = 3+3*5 所以x值当然是18了 10 }
带参数的宏 --------------------------------------------------------
形式:
#define 宏名称(参数列表) 替换主体
定义:
类函数宏的定义中,用圆括号括起来一个或多个参数,随后这些参数出现在替换部分
例子:
1 #define MAX(x,y) (x>y? x: y)
#运算符 ----------------------------------------------------------
形式:
#define 宏名称(参数列表) 替换主体
定义:
#符号把语言符号字符串化(也就是 用 "宏参数" 去替换替换主体中的 #宏参数 )
例子:
1 //定义宏 2 #define PSQR(x) cout<<"it is " #x<<endl; 3 4 //然后调用PSQR 5 PSQR(x) //输出的结果为: it is x
##运算符 --------------------------------------------------------
形式:
#define 宏名称(参数列表) 替换主体
作用:
- ##后面紧紧跟着的是宏参数
- 这个语言符号组合成单个语言符号,也就是把替换主体与宏参数结合成一个语言符号
- 宏参数在编译阶段只简单看成字符或字符串没有其他含义
例子:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 // 例1 ---- 2 //定义宏 3 #define XNAME(n) a##n 4 5 //调用宏 6 char a1 = 'x'; 7 cout<<XNAME(1)<<endl; //输出结果为: x 8 9 10 11 // 例2 ---- 12 //定义宏 13 #define XNAME(n) a##n = '6' 14 15 //调用宏 16 char a1 = 'x'; 17 XNAME(1);
取消宏定义#undef -------------------------------------------------
形式:
#undef 宏名称
作用:
取消定义过的宏
例子:
1 #define TWO 2 2 #undef TWO // 取消宏定义在以后的地方TWO这个宏就不存在了
预定义宏 -----------------------------------------------------------
- 本机日期 __DATE__
- ‘1’为遵循c标准 __STDC__
- 文件名 __FILE__
- 为本机环境设置1,否则为0 __STDC_HODTED__
- 行号 __LINE__
- 为c99时设置为1999901L __STDC
- 本机时间 __TIME__
杂项 ---------------------------------------------------------------
作用域:
作用域是文件,或者直到遇到undef为止
注意事项:
- 宏的名字不能有空格,并用圆括号括住每个参数,例如#define f (x) ((x)+1)尽管你想表达的是f(x)用((x)+1)的意思但是结果编译器却认为是f用(x)((x)+1)代替
- 宏不是函数,宏在编译时期只是做简单的替换
-
宏不是类型定义,因为它只作简单替换,对于类型定义最好用typedef来完成,比如#define F int*,与typedef int* F时用F去声明变量时, F a,b.宏只能做到把第一个变量声明成指针,而后一却是声明变量.而typedef可以把两个都声明成指针(关于typedef)
- 如果使用宏,加快程序运行速度,首先确定是否会引起重大差异
优缺点:
预处理器不对宏进行计算,而只是简单进行字符串替换,宏不检查其中变量类型,但宏会产生内联代码,而且比内联函数花费的时间多,比函数占的内存更大
其他的预编译
#line --------------------------------------------------------------
作用
- #line命令是用于更改__LINE__ 和 __FILE__变量的值. 文件名是可选的. __LINE__ 和 __FILE__ 变量
- 描述被读取的当前文件和行. 命令 <例:#line 10,“cool,cpp”//把行号重置为10,文件名为“cool.cpp”>
#error ------------------------------------------------------------
作用
使预处理器发出一条错误消息,该消息包含指令文本
条件编译 -----------------------------------------------------------
说明:
#ifdef ,#else , #elif , #ifndef , #if 和 #endif (用法与if…else相似)
作用:
可以防止头文件被重复定义
例子:
防止头文件被多次定义的例子:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //防止头文件被多次定义 2 //以 iostream 头文件为例子 3 // 方法1 ---- 4 #ifndef __IOSTREAM_H__ 5 #define __IOSTREAM_H__ 1 6 #include <iostream> 7 #endif 8 9 10 // 方法2 ---- 11 #pragma once 12 #include <iostream> 13 14 15 // 方法3 ---- 16 #if !defined(__IOSTREAM_H__) 17 #define __IOSTREAM_H__ 1 18 #include <iostream> 19 #endif 20 //一般不提倡这么用就是了
再来看一个来自于微软技术资源库的例子:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 // 例1 -- 2 #if defined(CREDIT) //defined(CREDIT) 代表的是是否定义过CREDIT这个宏 3 //如果定义过返回真,没定义返回假 4 credit(); 5 #elif defined(DEBIT) 6 debit(); 7 #else 8 printerror(); 9 #endif 10 11 12 //例子2 -- 13 #if DLEVEL > 5 //#if 后面也可以接条件表达式 14 #define SIGNAL 1 15 #if STACKUSE == 1 16 #define STACK 200 17 #else 18 #define STACK 100 19 #endif 20 #else 21 #define SIGNAL 0 22 #if STACKUSE == 1 23 #define STACK 100 24 #else 25 #define STACK 50 26 #endif 27 #endif 28 #if DLEVEL == 0 29 #define STACK 0 30 #elif DLEVEL == 1 31 #define STACK 100 32 #elif DLEVEL > 5 33 display( debugptr ); 34 #else 35 #define STACK 200 36 #endif
这里就简单的让你了解下条件编译的运用,例子的链接为:http://technet.microsoft.com/zh-cn/library/ew2hz0yd(v=vs.110)
杂项 ---------------------------------------------------------------
“预编译命令”的两大步骤:
- 先进行一次“预编译对所有预编译命令进行处理,把#include包含的头文件内容调出来放到#include命令的位置
- 进行正式编译工作,得到目标文件(后缀为obj)(不要把符号常量与变量混淆,符号常量只是一个符号,不占存储单元,习惯上,符号常量名用大写,变量名用小写,以好区分,使用符号常量好处①含义清楚;②在需要时可改)