翻译自http://en.cppreference.com/w/c/language/translation_phases
C编译器通过下面的这几个阶段处理C源文件。不同的编译器实际的实现过程可能有所不同,但是主要行为都是一致的。
阶段1
1)源文件(往往是一个由像UTF-8这样的多字节编码的文本文件)的字节都要做映射,通过某种方式将字符映射到源字符集中的字符。特别的,和操作系统相关的行结尾符统一由newline字符代替。
源字符集是一个多字节字符集,它将基本源字符集作为单字符子集,由下面的96个字符组成:
a). 5个空白字符(空格,水平tab,垂直tab,formfeed,new-line)
b). 10个数字字符(从’0’到’9’)。
c). 52个字母,从’a’到’z’和从’A’到’Z’。
d). 29个标点符号。2)Trigraph序列由对应的单字符表示代替。
阶段2
1)当反斜杠出现在行的末尾(紧随newline),反斜杠和newline都将被删除,将两个物理行组合成一个逻辑行。这是一个单次操作:如果某行以两个反斜杠结尾并且紧跟一个空行,并不会将三行组织成一行。
2)如果一个非空源文件在这个阶段之后并不是以一个newline结尾(无论是本来就没有newline还是以反斜杠结尾),行为将是没有定义的。阶段3
1) 源文件被分解成注释、空白字符(空格,水平tab,newline,垂直tab,formfeed)序列、以及下列预处理标记
a) 头文件名字:<stdio.h>
或者“myfile.h”
b) 标识符(函数名,变量名等等)
c) 数字
d) 字符常量或者字符串
e) 操作符和标点器(包括替代标记),比如+
,<<=
,<%
,##
, 或者and
f) 其它2) 每个注释用一个空格代替。
3) newline保留,其它非newline空白字符序列可能根据实际实现由一个空格代替。阶段4
1) 运行预处理器。
2) 由include指令引入的文件,递归的执行第一到第四阶段
3) 这个阶段之后,所有的预处理指令都从源文件中移除了。阶段5
1) 所有的字符和字符常量中的转义序列以及字符串都从源字符集转换到执行字符集(也许是多字节字符集,比如UTF-8,只要阶段1中列出的基本源字符集有单字节表示就行)。如果由转义序列确定的字符不是可执行字符集中的一部分,结果将因为实现的不同而不同,但是必须保证不是一个空或宽字符。注意:这个阶段中运行的转换在某些实现中可以通过命令行来控制它的行为,gcc和clang用
-finput-charset
来确定源字符集的编码,用-fexec-charset
和-fwide-exec-charset
来分别确定字符串中可执行字符集的编码,以及没有编码前缀的字符(从C11)。阶段6
仅由空字符隔开的字符串被链接起来。阶段7
编译开始:通过语法语义分析,翻译成翻译单元。阶段8
链接:翻译单元和库被集合成一个满足引用要求的程序镜像(它包含执行环境需要的信息)。