第十六章 - DDL-Killer/The-road-of-Linxu-Group2024 GitHub Wiki

翻译程序的第一步

  1. 把源代码中出现的字符映射到源字符集
  2. 编译器定位每个反斜杠后面跟着的实例,删除
  3. 编译器把文本划分预处理记号序列、空白序列和注释序列(编译器用一个空格字符替换每一条注释)

明示常量:#define

  • 第一部分是#define本身;第二部分是选定的缩写,也称为,宏不允许有空格,必须遵循C变量的命名规则;第三部分是称为替换列表替换体
  • 从宏变成最终替换文本被称为宏展开

记号

  • 从技术层面看,可以把宏的替换体看作是记号型字符串
  • 记号型字符串会省略空格,字符型字符串会保留空格

重定义常量

  • 先定义,再在文件中定义,这个过程称为重定义常量

在#define中使用参数

  • 可以创建外形和作用与函数类似的类函数宏
  • 类函数宏定义的圆括号可以有一个或多个参数 #define MEAN(X,Y) (((X)+(Y))/2)
  • 宏定义运算容易造成混乱,需要多个括号嵌套
  • 最好不要在宏中使用递增或递减运算符

用宏参数创建字符串:#运算符

  • C允许在字符串中包含宏参数,在类函数宏的替换体中,#作为一个预处理运算符,可以把记号转换为字符串,把宏形参转换为字符串中形参的过程称为字符串化

预处理器粘合剂:##运算符

  • ##运算符可以把两个记号组合成一个记号

宏和函数的选择

  • 宏生成内联代码,在程序中生成语句
  • 函数只有一份函数语句的副本,节省了更多空间,但是必须先跳转至函数内,随后再返回主调函数,花费时间大
  • 宏的另一个优点是,不用担心变量类型

文件包含:#include

  • 两种形式:<>和""

使用头文件

  1. 明示常量
  2. 宏函数
  3. 函数声明
  4. 结构模板定义
  5. 类型定义

其他定义

#undef指令

  • 用于取消定义的#define指令,可以重新定义

条件编译

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

预定义宏

image

内联函数

  • 把函数变成内联函数建议尽可能快地调用该函数,其具体效果由实现定义
  • 标准规定具有内部链接的函数可以称为内联函数,还规定了内联函数的定义与调用该函数的代码必须再同一个文件中

C库

如何访问?

  1. 自动
  2. 文件包含
  3. 库包含

使用库描述

  • 阅读文档的关键式看懂函数头

数学库

通用工具库

atexit()函数

  • 这个函数使用函数指针
  • 把退出时需要调用的函数地址传给atexit(),在调用exit()时调用(最后添加的先执行)

exit()函数

  • 执行完atexit()函数之后,完成一些清理工作
  • 刷新所有输出流
  • 关闭所有打开的流
  • 关闭tmpfile()创建的临时文件
  • 把控制权移交给主机环境

qsort()函数

  • 把数组分为更小的数组,直到变成单元素数组
  • 首先把数组分为两部分,一部分的值都小于另一部分的值

断言库

  • assert.h支持辅助调整程序,由assert()宏组成,表达式出现错误,写出错误信息,使用abort()函数终止
  • 写#define NDEBUG在前面重新编译,就能关闭

memcopy()和memmove()

  • memcopy假定没有重叠,通过缓冲直接拷贝,如果出现重叠,结果未知
  • 第三个参数指明待拷贝的字节数