C 基础 - 预处理器与C库
翻译程序的第一步:
首先:编译器把源代码中出现的字符映射到源字符集。
第二:编译器定位每个 \ 后面跟着换行符实例,并删除它们。
第三:编译器把文本划分成处理记号序列、空白序列和注释序列。
最后:准备进入预处理阶段,查找#号开始的预处理指令。
在编译时打开编译的 -p 选项,可以看到预处理器的输出。
预处理指令类型:
* 宏定义
* 文件包含
* 条件编译
#define 明示常量
#define 标识符 替换列表
C预处理器在程序执行之前查看程序。
预处理器不做计算,不对表达式求值,只进行替换。
类对象宏
#define (符号常量), 定义时组成部分如下:
* #deinfine 符号指令
* 宏
* 替换列表
对于大部分的数字常量,可以使用符号常量。
类函数宏
#define 标识符(x1, x2, ... xn) 替换列表
#define 中还可以使用参数
#define SQUARE(X) X*X z = SQUARE(2);
练习: 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)
#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
练习: 写一个"标准"宏MIN ,这个宏输入两个参数并返回较小的一个。
#define MIN(A,B) ( (A) <= (B) ? (A) : (B) )
练习:
有如下带参数宏定义
#define TAILQ_HEAD(name, type) \ struct name { \ struct type *tqh_first; /* first element */ \ struct type **tqh_last; /* addr of last next element */ \ }
有如下声明
TAILQ_HEAD(, tailq_entry) my_tailq_head;
宏展开后结构
struct { struct tailq_entry *tqh_first; struct tailq_entry **tqh_last; } my_tailq_head;
在编译时,使用 -E 选项来编译,查看
gcc -O2 -o tailq_ex tailq_ex.c -E
文件包含: #include 指令
当预处理器发现#include指令时,会查看后面的文件名把文件的内容包含到当前文件中。
const与#define 相比,有何优点?
1) const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
2) 有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。