文章插图
语言的编译过程中第一步进行的就是预编译了,预编译中就是执行#开头的语句,这些处理就是本篇总结的知识点,预处理和条件编译 。
通俗的讲预处理就是编译器自动的帮我们上去整理一遍代码,它依据的规则(我们交代给他的嘱托)就是预处理指令和条件编译的指令 。
编译器根据指令,将需要包含的代码整体复制放到要包含的文件中,检查条件编译的条件是否成立,删除用不到的代码,留下有用的代码,预处理的过程仅此而已 。
要让编译器准确的完成我们交给他的任务,我们就必须准确的给它下达正确的命令,这些命令就是下面总结的内容了 。只是常用到的知识点的总结,水平有限有错误在所难免欢迎指正 。
1.预处理是c文件在编译前的处理过程 。
这个过程包括如下几个内容:
- 删除注释
- 插入被#include包含的文件的内容
- 替换有#define定义的符号
- 确认条件编译具体该进行编译的部分
这些符号是编译器自带的,不用我们自己定义,直接使用就可以 。
一般在我们调试程序时,打印这些信息时使用它们 。
- __FILE__ :要进行编译的文件的文件名
- __LINE__:文件该符号所在行得行号
- __DATE__:文件被编译的日期
- __TIME__:文件被编译的时间
- 为数值命名一个符号 。
用法:
- 一般在程序的头部定义该语句,之后就可以用name去代替stuff写代码了 。编译时语句中如果出现name,就会被预编译器替换成stuff 。
Eg1:
#define Ret returnval 为简化名字的书写和阅读方便
Eg2:
#define Loop while(1); 可以替换一个语句,替换语句时注意“;”的问题
4.宏的使用
所谓的宏,其实是#define的文本替换的活用 。
本质是是在文本替换的基础上,再追加一步,参数的替换 。
Eg 1:
#define MAX(a,b) ( (a)>(b)?(a):(b))
在代码中的使用过程是这样的:
第一步:文本替换
maxnum=MAX(2,4); —> ( (a)>(b)?(a):(b));
第二步:参数代入
maxnum=( (2)>(4)?(2):(4));
到此预处理器的宏处理阶段完成 。
5.宏的参数代入过程看起来很像是与函数的参数传递过程,有什么不一样呢?
- 1.宏的参数不限制类型使用参数时也不需要声明类型,但是函数的参数必须声明(规定)参数类型 。
Eg :
#define MALLOC(n,type) ( (type)*) malloc( (n) * sizeof(type) ) )
带个数的结果:MALLOC(5,int ) —>(int*)malloc( (5)*sizeof(int) )
这里的参数type,函数是无法用参数进行传递的 。
- 2.实现机制不同,宏是通过预处理时文本替换实现,函数是通过运行时临时调用和返回,这就带来他们的优缺点:
- 宏:不需要调用开销,执行频繁的简单计算,缺点会增加代码体积 。
- 函数:比较耗费系统资源,但不增加代码体积 。
#:将将宏参数插入字符串中 。
Eg :
#define PRINT(a) pirntf("the value of a is %d.n",(a) )
int b=2;
PRINT(b); => the value of a is 2.
#define PRINT(a) pronrf("the calue of "#a"is %d.n",(a) )
int b=2;
PRINT(b); => the value of b is 2.
- ##:粘合剂 字符合并
#define RENAME(A) NewName ## A
RENAME(3); => NewName3
7.宏的注意点:
- 宏名全大写字母表示
- 每个参数必须加()
- 宏的整体外面加一层括号
- 不要在宏的末尾加分号
- 替换文本(stuff)的内容太多可以用“”连接换行
- 宏定义时:宏的参数列表(括号的左边)与宏名(name)之间不能有空格 。在代码中使用无此规定
- 宏的参数不能用 自增、自减等运算符,会有副作用
- 用法1:
statements
#endif
Eg :常用的调试语句
#define DEBUG 1 //1:打开,0:屏蔽
#if DEBUG
printf("File is %s,Line is %d",__FILE__,__LINE__);
#endif
这样debug语句不会 影响程序本身运行 。