预处理指令
1.预定义符号
预定义符号是在预处理阶段处理的。
1.__FILE__ // 进⾏编译的源⽂件2.__LINE__ // ⽂件当前的⾏号3.__DATE__ // ⽂件被编译的⽇期4.__TIME__ // ⽂件被编译的时间5.__STDC__ // 如果编译器遵循 ANSI C ,其值为 1 ,否则未定义
2.#define 定义常量
# define name stuff
3.#define定义宏
define定义宏和定义常量的区别就是定义宏有参数,这种实现通常称为宏(macro)或定义宏
宏的申明方式:
# define name( parament-list ) stuff
4.带有副作用的宏参数
带有副作用的宏参数比如++ --操作符,在参数数量超过1个时会导致问题
比如:
这里得到的是7 4 6 也是因为宏的处理是替换而不是函数的先计算再传值。
这里经过预处理后:
(x++) > (y++) ? (x++) : (y++);
5.宏替换的规则
1.在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先
3.最后,再次对结果⽂件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上
2.当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
6.宏和函数的对比
两个求最大值的方法对比下,宏的方法更好一点:
1.宏的处理只涉及到计算,而函数涉及到函数的调用、计算、函数的返回。因此函数的时间开销大一点
2.宏的替换原则并没有参数限制,而函数针对于不同的参数要写不同的版本。
函数的优势:
7.#和##
#运算符
#并不是#include或者#define的#
## 运算符
8.命名约定
由于宏和函数的相似性,所以为了区分它们两个,一般的:
宏的字母全部大写
函数一般是首字母大写
9.#undef
当我们定义的宏在某时段不用了或者需要重定义,那么这就需要把原有的定义取消掉。
这就需要用到#undef
10.命令行定义
假设我们的代码要根据不同的机器设置不同的版本,就像前面的位段一样,就要用的命令行代码
这里简单的拿一个数组大小举例:
我们并在写的时候并不直接将ARRAY_SIZE定义。而是在使用的时候给一个大小。
//linux 环境演示。gcc -D ARRAY_SIZE= 10 programe.c
11.条件编译
条件编译顾名思义就是看情况编译,这跟if语句很像,但是它们的处理阶段不同,一个是在预处理阶段,一个是运行阶段。
比如我们在编译的时候需要有调试语句,但是不用的时候删掉再写一遍又很麻烦,所以我们选择性编译:
常见的形式:
1.
#if 常量表达式
//...
#endif
2.多分支条件的编译
#if 常量表达式
//...
#elif 常量表达式
//...
#else
//...
#endif
3.判断是否被定义
#if defined(symbol)#ifdef symbol
#if !defined(symbol)
#ifndef symbol
4.嵌套指令#if defined(OS_UNIX)#ifdef OPTION1unix_version_option1();#endif#ifdef OPTION2unix_version_option2();#endif#elif defined(OS_MSDOS)#ifdef OPTION2msdos_version_option2();#endif#endif
12.头文件的包含
头文件有两种包含方式:
1.本地头文件包含
包含方式:
先从自己所在的路径搜索,如果没有就在存放标准库的文件中搜索。
2.库函数的头文件包含
包含方式:
直接从存放标准库的文件中搜索。
3.嵌套文件包含
像这样的多次包含是会出现的,而且编译器在预处理的时候也会真的把他们都拷过来。
如何避免呢?