语言的编译过程中第一步进行的就是预编译了,预编译中就是执行#开头的语句,这些处理就是本篇总结的知识点,预处理和条件编译。
通俗的讲预处理就是编译器自动的帮我们上去整理一遍代码,它依据的规则(我们交代给他的嘱托)就是预处理指令和条件编译的指令。
编译器根据指令,将需要包含的代码整体复制放到要包含的文件中,检查条件编译的条件是否成立,删除用不到的代码,留下有用的代码,预处理的过程仅此而已。
要让编译器准确的完成我们交给他的任务,我们就必须准确的给它下达正确的命令,这些命令就是下面总结的内容了。只是常用到的知识点的总结,水平有限有错误在所难免欢迎指正。
1.预处理是c文件在编译前的处理过程。
这个过程包括如下几个内容:
2.预定义器定义的符号(现成的)
这些符号是编译器自带的,不用我们自己定义,直接使用就可以。
一般在我们调试程序时,打印这些信息时使用它们。
3.#define 定义符号名的用法
Eg: #define name stuff (很容易跟typedef搞混前后顺序)
用法:
b. 文本替换
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.宏的参数代入过程看起来很像是与函数的参数传递过程,有什么不一样呢?
宏的这个特点有时可以带入函数无法带入的参数类型,进行运算。
Eg :
#define MALLOC(n,type) ( (type)*) malloc( (n) * sizeof(type) ) )
带个数的结果:MALLOC(5,int ) —>(int*)malloc( (5)*sizeof(int) )
这里的参数type,函数是无法用参数进行传递的。
6.#运算符和##运算符
#:将将宏参数插入字符串中。
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.
Eg :
#define RENAME(A) NewName ## A
RENAME(3); => NewName3
7.宏的注意点:
8.条件编译
#if 常量表达式
statements
#endif
Eg :常用的调试语句
#define DEBUG 1 //1:打开 ,0:屏蔽
#if DEBUG
printf("File is %s,Line is %d",__FILE__,__LINE__);
#endif
这样debug语句不会 影响程序本身运行。
#if 常量表达式
Statements;
#elif 常量表达式
Statements;
…
#elif 常量表达式
Statements;
#else
Statements;
#endif
预处理器从第一条依次判断常量表达式的真假(0为假),
9.文件包含
#ifndef _FILENAME_H
#define _FILENAME_H
XXXX(其他语句)
#endif
来防止重复包含
在结束的时候,还是再说一下学习过程中的感想,预编译这块很容易被我们忽视,初学起来感觉难度并没有很大,但是这里可以深究的地方还是很多的,多看一看这方面的例子,或者稍复杂些的宏定义,就会有很容易掉坑里的感觉,而且这里出现的Bug是很不容易被发现的。在程序编写时,恰当的使用宏和条件编译会让程序的结构得到优化,在uboot中大规模的使用宏定义条件编译相关的语句来实现配置选择和功能的选择,所以说这部分的知识很重要,需要取细细研究。争取做到准确,熟练,快速的看懂,看的对宏的意图!