第十六章 - DDL-Killer/The-road-of-Linxu-Group2024 GitHub Wiki
翻译程序的第一步
- 把源代码中出现的字符映射到源字符集
- 编译器定位每个反斜杠后面跟着的实例,删除
- 编译器把文本划分预处理记号序列、空白序列和注释序列(编译器用一个空格字符替换每一条注释)
明示常量:#define
- 第一部分是#define本身;第二部分是选定的缩写,也称为宏,宏不允许有空格,必须遵循C变量的命名规则;第三部分是称为替换列表或替换体
- 从宏变成最终替换文本被称为宏展开
记号
- 从技术层面看,可以把宏的替换体看作是记号型字符串
- 记号型字符串会省略空格,字符型字符串会保留空格
重定义常量
在#define中使用参数
- 可以创建外形和作用与函数类似的类函数宏
- 类函数宏定义的圆括号可以有一个或多个参数
#define MEAN(X,Y) (((X)+(Y))/2)
- 宏定义运算容易造成混乱,需要多个括号嵌套
- 最好不要在宏中使用递增或递减运算符
用宏参数创建字符串:#运算符
- C允许在字符串中包含宏参数,在类函数宏的替换体中,#作为一个预处理运算符,可以把记号转换为字符串,把宏形参转换为字符串中形参的过程称为字符串化
预处理器粘合剂:##运算符
宏和函数的选择
- 宏生成内联代码,在程序中生成语句
- 函数只有一份函数语句的副本,节省了更多空间,但是必须先跳转至函数内,随后再返回主调函数,花费时间大
- 宏的另一个优点是,不用担心变量类型
文件包含:#include
使用头文件
- 明示常量
- 宏函数
- 函数声明
- 结构模板定义
- 类型定义
其他定义
#undef指令
条件编译
- #ifdef、#else、和#endif
- #ifdef指令说明,如果预处理器定义了标识符,则执行#else和#endif前的所有指令并编译所有的C代码

- #ifndef指令
- #ifndef指令说明,如何预处理器没有定义标识符,则执行#else和#endif前的所有指令并编译所有的C代码
- #if和#elif指令
- 后面跟整型常量表达式,如果表达式为非零,则表达式为真
- 类似if和else
预定义宏

内联函数
- 把函数变成内联函数建议尽可能快地调用该函数,其具体效果由实现定义
- 标准规定具有内部链接的函数可以称为内联函数,还规定了内联函数的定义与调用该函数的代码必须再同一个文件中
C库
如何访问?
- 自动
- 文件包含
- 库包含
使用库描述
数学库
通用工具库
atexit()函数
- 这个函数使用函数指针
- 把退出时需要调用的函数地址传给atexit(),在调用exit()时调用(最后添加的先执行)
exit()函数
- 执行完atexit()函数之后,完成一些清理工作
- 刷新所有输出流
- 关闭所有打开的流
- 关闭tmpfile()创建的临时文件
- 把控制权移交给主机环境
qsort()函数
- 把数组分为更小的数组,直到变成单元素数组
- 首先把数组分为两部分,一部分的值都小于另一部分的值
断言库
- assert.h支持辅助调整程序,由assert()宏组成,表达式出现错误,写出错误信息,使用abort()函数终止
- 写#define NDEBUG在前面重新编译,就能关闭
memcopy()和memmove()
- memcopy假定没有重叠,通过缓冲直接拷贝,如果出现重叠,结果未知
- 第三个参数指明待拷贝的字节数