网站首页 > 技术文章 正文
编译器处理 C++ 源文件时,如同严格按照以下顺序进行各个阶段的处理:
阶段 1
1) 将源文件的各个单独字节(以具体实现所定义的方式)映射为基本源字符集的字符。特别是,操作系统相关的行尾指示符均被替换为换行字符。2) 可以接受的源文件字符的集合由实现定义。 (C++11 起)任何无法被映射到基本源字符集中的字符的源文件字符均被替换为它的通用字符名(用 \u 或 \U 转义),或使用某种(由实现所定义的)等效处理的方式。3) 将各个三标符序列替换为它对应的单字符表示。(C++17 前) | (C++23 前) |
保证至少支持 UTF-8 代码单元的序列的输入文件(UTF-8 文件)。其他支持的输入文件的种类的集合由实现定义。该集合不为空时,决定文件种类的方式通过由实现定义且以与内容无关的方式决定(包括指定输入文件为 UTF-8 文件,只识别字节序标记无法满足该要求)。如果决定文件是 UTF-8 文件,那么它必须是格式正确的 UTF-8 代码单元序列。解码该文件会得到一个 Unicode 标量值序列,然后通过将每个 Unicode 标量映射到对应的翻译字符集元素来组成翻译字符集元素序列。在结果序列中,输入序列中每对回车(U+000D)后随换行符(U+000A)的字符对,以及每个不后随换行符(U+000A)的回车(U+000D),都会替换成一个换行字符。对于其他支持的文件格式,将字符(以实现所定义的方式)映射为翻译字符集中的字符的序列。特别是,操作系统相关的行尾指示符均被替换为换行字符。 | (C++23 起) |
阶段 2
1) 如果第一个翻译字符是字节序标记(U+FEFF),那么将它删除。 (C++23 起)当反斜杠(\)在行尾(其后紧跟零或多个除换行符外的空白符,再紧跟 (C++23 起)换行符)出现时,删除这些字符并将两个物理源码行组合成一个逻辑源码行。这是单趟操作:如果有一行以两个反斜杠结束且后随一个空行,这三行不会合为一行。
2) 如果在此步骤后非空源文件不以换行符结束(此时行尾反斜杠不再是拼接点),那么在最后添加一个换行符。
阶段 3
1) 将源文件分解为注释,空白字符(空格、水平制表、换行、垂直制表和换页)的序列,和下列各种预处理记号:
a) 标头名,如 <iostream> 或 "myfile.h"
b) 预处理 import 和 module 指令(即 import XXX; 和 module XXX;)的过程中产生的占位记号 | (C++20 起) |
c) 标识符
d) 预处理数字
e) 字符字面量,包括用户定义的字符字面量 (C++11 起)
f) 字符串字面量,包括用户定义的字符串字面量 (C++11 起)
g) 运算符和标点(包括代用记号),如 +、<<=、<%、## 或 and
h) 不属于任何其他类别的单独非空白字符
如果匹配此类别的字符是以下之一,那么程序非良构:
- 撇号(',U+0027)
- 引号(",U+0022)
- 基本字符集以外的字符
2) 撤回在任何原始字符串字面量的首尾双引号之间在阶段 1 和 (C++23 前)阶段 2 期间进行的所有变换。 | (C++11 起) |
3) 以一个空格字符替换每段注释。
保留换行符。未指明是否可以将非换行空白字符序列缩减成单个空格字符。
在组成预处理记号而吸收字符时(即不组成注释或其他形式的空白),通用字符名会被识别并被翻译字符集中的指定元素替换,除非正在匹配以下内容中的字符序列:a) 字符字面量(c字符序列)b) 字符串字面量(s字符序列 和 r字符序列),但不包括分隔符(d字符序列)c) 要包含的文件名(h字符序列 和 q字符序列) | (C++23 起) |
如果一个给定字符前的输入已被解析为预处理记号,下一个预处理记号通常会由能构成预处理记号的最长字符序列构成,即使这样处理会导致后续分析失败。这常被称为最大吞噬。
int foo = 1;
int bar = 0xE+foo; // 错误:非法的预处理数字 0xE+foo
int baz = 0xE + foo; // OK
int quux = bar+++++baz; // 错误:bar++ ++ +baz,而非 bar++ + ++baz。
最大吞噬规则只 (C++11 前)有以下例外:
如果以下一个字符开头的字符序列可作为原始字符串字面量的前缀和起始双引号,那么下个预处理记号应当为原始字符串字面量。该字面量由匹配原始字符串模式的最短字符序列组成。#define R "x" const char* s = R"y"; // 非良构的原始字符串字面量,而非 "x" "y" const char* s2 = R"(a)" "b)"; // 原始字符串字面量后随普通字符串字面量如果接下来的三个字符是 **<::**且后继字符不是 **:** 或者 **>**,那么把 **<** 自身当做预处理记号(而非代用记号 <: 的首字符)。struct Foo { static const int v = 1; }; std::vector<::Foo> x; // OK,<: 未被当作 [ 的代用记号 extern int y<::>; // OK,同 extern int y[]。 int z<:::Foo::value:>; // OK,int z[::Foo::value]; | (C++11 起) |
- 头文件名预处理记号只会在 #include 或 import (C++20 起)指令,或者在 __has_include 表达式}}中形成。
std::vector<int> x; // OK,<int> 不是头文件名
阶段 4
1) 执行预处理器。
2) #include 指令所引入的每个文件都经历阶段 1 到 4 的处理,递归执行。
3) 此阶段结束时,所有预处理器指令都应从源(代码)移除。
阶段 5
1) 将字符字面量及字符串字面量中的所有字符从源字符集转换到执行字符集(可以是 UTF-8 这样的多字节字符集,只要基本源字符集的 96 个字符都拥有单字节表示即可)。2) 将字符字面量和非原始字符串字面量中的转义序列和通用字符名展开,并转换到执行字符集。 如果某个通用字符名所指定的字符不是执行字符集的成员,那么结果由实现定义,但保证不是空(宽)字符。注意:某些实现能以命令行选项控制此阶段所进行的转换:gcc 和 clang 用 -finput-charset 指定源字符集的编码,用 -fexec-charset 和 -fwide-exec-charset 指定无编码前缀的 (C++11 起)字符串和字符字面量中的执行字符集的编码,而 Visual Studio 2015 Update 2 及之后版本分别用 /source-charset 和 /execution-charset 指定源字符集和执行字符集。 | (C++23 前) |
对于每个含有多个相邻字符串字面量记号的序列,都会有一个以此规则指定的共同编码前缀。其中每个字符串字面量记号都会被视为拥有该共同编码前缀。 (字符转换改为在阶段 3 执行) | (C++23 起) |
阶段 6
拼接相邻的字符串字面量。
阶段 7
进行编译:将各个预处理记号转换成记号。将所有记号当作一个翻译单元进行语法和语义分析并进行翻译。
阶段 8
检验每个翻译单元,产生所要求的模板实例化的列表,其中包括显式实例化所要求的实例化。定位模板定义,并进行所要求的实例化,以产生实例化单元。
阶段 9
将翻译单元、实例化单元和为满足外部引用所需的库组件汇集成一个程序映像,它含有在它的执行环境中执行所需的信息。
注解
某些编译器不实现实例化单元(又称为模板仓库或模板注册表),而是简单地在阶段 7 编译每个模板实例化,将代码存储在它所显式或隐式要求的对象文件中,然后由链接器在阶段 9 将这些编译后的实例化缩减到一个。
- 上一篇: C++代码解析3(c++代码大全及其含义)
- 下一篇: 每日一见K8s笔记(三十六)service概述
猜你喜欢
- 2024-10-11 C++代码解析3(c++代码大全及其含义)
- 2024-10-11 C++ 将容器元素连接成字符串(c++中的容器)
- 2024-10-11 Qt/C++中英输入法/嵌入式输入法/小数字面板/简繁切换/特殊字符
- 2024-10-11 深入解析C++字符串处理:高效去除头尾空格的多种方法与实践
- 2024-10-11 C到C++转换学习笔记(c和c++转换)
- 2024-10-11 初识C++输入输出(c++基本的输入输出)
- 2024-10-11 如何从C语言快速过渡到C++?大神回答道:只需要一个下午就可以
- 2024-10-11 C++系列2-1:C++快速入门之命名空间和输入输出
- 2024-10-11 EasyC++01,C++程序概述(c++ ecs)
- 2024-10-11 C++第2课:输出与换行#少儿编程#盐城少儿编程#编程机器人
- 02-21走进git时代, 你该怎么玩?_gits
- 02-21GitHub是什么?它可不仅仅是云中的Git版本控制器
- 02-21Git常用操作总结_git基本用法
- 02-21为什么互联网巨头使用Git而放弃SVN?(含核心命令与原理)
- 02-21Git 高级用法,喜欢就拿去用_git基本用法
- 02-21Git常用命令和Git团队使用规范指南
- 02-21总结几个常用的Git命令的使用方法
- 02-21Git工作原理和常用指令_git原理详解
- 最近发表
- 标签列表
-
- cmd/c (57)
- c++中::是什么意思 (57)
- sqlset (59)
- ps可以打开pdf格式吗 (58)
- phprequire_once (61)
- localstorage.removeitem (74)
- routermode (59)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- log.warn (60)
- cannotinstantiatethetype (62)
- js数组插入 (83)
- resttemplateokhttp (59)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- reader.onload (61)
- outofmemoryerror是什么意思 (64)
- flask文件上传 (63)
- eacces (67)
- 查看mysql是否启动 (70)
- java是值传递还是引用传递 (58)
- 无效的列索引 (74)