预处理
预处理器执行宏替换 条件编译以及包含指定的文件 以#开头的命令行(#前可以有空格)就是预处理器处理的对象 预处理过程可以划分为几个连续的阶段 1.将三字符序列转换为等价字符。如果操作系统需要,还要在源文件的各行之间插入换行符 2.将指令行中位于换行符前的反斜杠删除,以把各指令行连接起来 3.将程序分成用空白符分隔的记号,注释将被替换为一个空白符。 接着执行预处理指令,并进行宏替换 4.将字符串常量和字符串字面量中的转义字符序列替换为等价字符,然后把相邻的字符串字面值连接起来 5.收集必要的程序和数据,并将外部函数和对象的引用与其定义相连接,翻译经过以上处理得到的结果,然后与其他程序和库连接起来
12.1 三字符序列
C语言源程序的字符集是7位ASCII码的子集,但它是ISO 646-1983不变代码集的超集 为了将程序通过这种缩减的字符集表示出来,下列所示的所有三字符序列都要用相应的单个字符替换 这种替换在进行所有其他处理之前进行 ??=
12.2行连接
通过将以反斜杠结束的指令行末尾的反斜杠和其后的换行符删除掉,可以将若干指令行合并成一行 这种处理要在分隔记号之前进行
12.3宏定义和扩展
类似于下列形式的控制指令 #define 标识符 记号序列 将使得预处理器把该标识符后续出现的各个实例用给定的记号序列替换 记号序列前后的空白符都将被丢弃掉 第二次用#define指令定义同一标识符是错误的 类似于下列形式的标识符 #define 标识符(标识符表) 记号标记 是一个带有参数的宏定义,其中第一个标识符与圆括号(之间没有空格) 同第一种形式一样,记号序列前后的空白符都将被丢弃。 如果要对宏进行重定义,则必须保证其参数个数 拼写及记号序列都与前面的定义相同 #undef 用于取消标识符的预处理器定义 将#undef应用于未知标识符并不会导致错误 按照第二种形式定义宏时,标识符及其后用一对圆括号括起来,由逗号分隔的记号序列就构成了一个宏调用 宏调用的实际参数是用逗号分隔的记号序列 用引号或嵌套的括号括起来的逗号不能用于实际参数 在处理的过程中,实际参数不进行宏扩展 宏调用时,实际参数的数目必须与定义中形式参数的数目匹配
12.4文件包含
#include <文件名> 把该行替换为文件名指定的文件的内容 #include "文件名" 从源文件的位置开始搜索指定文件,如果没有找到指定的文件,则按照第一种方式处理 #include 记号序列 #include 文件可以嵌套
12.5条件编译
if行: #if 常量表达式 #ifdef 标识符 #ifndef 标识符 elif部分: elif行 文本 elif部分 elif行 #elif 常量表达式 else 部分 else 行 文本 else 行 # else
12.6行控制
为了便于其他预处理器生成C语言 #line 常量 ' 文件名" #line 常量 将使编译器认为: 下一行源代码的行号是以十进制常量的形式给出的 并且,当前的输入文件是由该标识符命名的
12.7错误信息生成
#error 记号序列 使预处理器打印包含该记号序列的诊断信息
12.8pragma
#pragma 记号序列 将使预处理器执行一个与具体实现相关的操作 无法识别pragma将被忽略
12.9空指令
# 形式的预处理器将不执行任何操作
12.10预定义名字
某些标识符是预定义的,扩展后将生成特定的信息 它们同预处理器表达式运算符defined一样,不能取消定义或重新定义 __LINE__ 包含当前源文件函数 __FILE__ 包含正在编译的源文件的字符串 __DATE__ 编译日期 "Mmm dd yyyy" __TIME__ 编译时间 "hh:mm:ss" __STDC__ 整型常量1
13语法
存储类说明符:one of auto register static extern typedef 类型说明符:one of void char short int long float double signed unsigned 类型限定符:one of const volatile 结构或联合:one of struct union 枚举说明符: enum 常量: 整型常量 字符常量 浮点常量 枚举常量 控制指令: #define 标识符 记号序列 #define 标识符 (标识符表) 记号序列 #undef 标识符 #include <文件名> #include "文件名" #include 记号序列 #line 常量 "文件名" #line 常量 #error 记号序列 #pragma 记号序列