216 lines
27 KiB
HTML
216 lines
27 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh" dir="ltr" class="client-nojs" xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh">
|
|
<head><meta http-equiv="x-ua-compatible" content="ie=edge">
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
<title>翻译阶段</title>
|
|
<link rel="stylesheet" href="ext.css" />
|
|
<link rel="stylesheet" href="site_modules.css" />
|
|
</head>
|
|
<body class="mediawiki ltr sitedir-ltr ns-0 ns-subject page-cpp_language_translation_phases skin-cppreference2 action-view cpp-navbar">
|
|
<div id="cpp-content-base">
|
|
<div id="content"><a id="top"></a>
|
|
<h1 id="firstHeading" class="firstHeading">翻译阶段</h1>
|
|
<div id="bodyContent">
|
|
<div id="contentSub"><span class="subpages">< <a href="cpp.html">cpp</a>‎ | <a href="cpp-language.html">language</a></span></div>
|
|
<div id="mw-content-text" lang="zh" dir="ltr" class="mw-content-ltr" xml:lang="zh">
|
|
<p>编译器处理 C++ 源文件时,如同严格按照以下顺序进行各个阶段的处理:</p>
|
|
<h3><span class="mw-headline" id=".E9.98.B6.E6.AE.B5_1">阶段 1</span></h3>
|
|
<div class="t-li1"><span class="t-li">1)</span> (以实现定义方式)将源文件的各个单独字节映射为<span class="t-rev-inl t-until-cxx23"><span><a href="cpp-language-charset.html#.E5.9F.BA.E7.A1.80.E6.BA.90.E5.AD.97.E7.AC.A6.E9.9B.86">基础源字符集</a></span> <span><span class="t-mark-rev t-until-cxx23">(C++23 前)</span></span></span><span class="t-rev-inl t-until-cxx23"><span><a href="cpp-language-charset.html#.E7.BF.BB.E8.AF.91.E5.AD.97.E7.AC.A6.E9.9B.86">翻译字符集</a></span> <span><span class="t-mark-rev t-until-cxx23">(C++23 前)</span></span></span>的字符。特别是,操作系统相关的行尾指示符均被替换为换行字符。</div>
|
|
<div class="t-li1"><span class="t-li">2)</span> <span class="t-rev-inl t-since-cxx11"><span>可以接受的源文件字符的集合由实现定义。</span> <span><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></span></span><span class="t-rev-inl t-until-cxx23"><span>任何无法被映射到<a href="cpp-language-charset.html#.E5.9F.BA.E7.A1.80.E6.BA.90.E5.AD.97.E7.AC.A6.E9.9B.86">基础源字符集</a>中的字符的源文件字符均被替换为其<a href="cpp-language-escape.html">通用字符名</a>(用 <code>\u</code> 或 <code>\U</code> 转义),或某种被等价处理的由实现定义的形式。</span> <span><span class="t-mark-rev t-until-cxx23">(C++23 前)</span></span></span></div>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-until-cxx17">
|
|
<td>
|
|
<div class="t-li1"><span class="t-li">3)</span> 将各个<a href="cpp-language-operator_alternative.html">三标符序列</a>替换为其对应的单字符表示。</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-until-cxx17">(C++17 前)</span></td>
|
|
</tr>
|
|
</table>
|
|
<h3><span class="mw-headline" id=".E9.98.B6.E6.AE.B5_2">阶段 2</span></h3>
|
|
<div class="t-li1"><span class="t-li">1)</span> 当反斜杠出现于行尾(其后紧跟<span class="t-rev-inl t-since-cxx23"><span>零或多个除换行符外的空白符,再紧跟</span> <span><span class="t-mark-rev t-since-cxx23">(C++23 起)</span></span></span>换行符)时,删除这些字符并将两个物理源码行组合成一个逻辑源码行。这是单趟操作:如果有一行以两个反斜杠结束且后随一个空行,这三行不会合为一行。如果在这个阶段组成了<a href="cpp-language-escape.html">通用字符名</a>,那么行为未定义。</div>
|
|
<div class="t-li1"><span class="t-li">2)</span> 如果在此步骤后非空源文件不以换行符结束(无论是原本就没有换行,还是以反斜杠结束),那么<span class="t-rev-inl t-until-cxx11"><span>其行为未定义</span> <span><span class="t-mark-rev t-until-cxx11">(C++11 前)</span></span></span><span class="t-rev-inl t-since-cxx11"><span>在最后添加一个换行符</span> <span><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></span></span>。</div>
|
|
<h3><span class="mw-headline" id=".E9.98.B6.E6.AE.B5_3">阶段 3</span></h3>
|
|
<div class="t-li1"><span class="t-li">1)</span> 将源文件分解为<a href="cpp-comment.html">注释</a>,空白字符(空格、水平制表、换行、垂直制表和换页)的序列,和下列各种<i>预处理记号</i>:</div>
|
|
<div class="t-li2"><span class="t-li">a)</span> 头名,如 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy1"><</span>iostream<span class="sy1">></span></span></span> 或 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="st0">"myfile.h"</span></span></span></div>
|
|
<div class="t-li2"><span class="t-li">b)</span> <a href="cpp-language-identifiers.html">标识符</a></div>
|
|
<div class="t-li2"><span class="t-li">c)</span> 预处理数字</div>
|
|
<div class="t-li2"><span class="t-li">d)</span> <a href="cpp-language-character_literal.html">字符</a>和<a href="cpp-language-string_literal.html">字符串</a>字面量<span class="t-rev-inl t-since-cxx11"><span>,包括<a href="cpp-language-user_literal.html">用户定义</a>的</span> <span><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></span></span></div>
|
|
<div class="t-li2"><span class="t-li">e)</span> 运算符和标点(包括<a href="cpp-language-operator_alternative.html">代用记号</a>),如 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy2">+</span></span></span>、<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy1"><<=</span></span></span>、<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy1"><</span><span class="sy2">%</span></span></span>、<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="co2">##</span></span></span> 或 <span class="t-c"><span class="mw-geshi cpp source-cpp">and</span></span></div>
|
|
<div class="t-li2"><span class="t-li">f)</span> 不属于任何其他类别的单独非空白字符</div>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<div class="t-li1"><span class="t-li">2)</span> 撤回在任何原始<a href="cpp-language-string_literal.html">字符串字面量</a>的首尾双引号之间在阶段 1 和 2 期间进行的所有变换。</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<div class="t-li1"><span class="t-li">3)</span> 以一个空格字符替换每段注释。</div>
|
|
<p>保留换行符。未指明是否可以将非换行空白字符序列缩减成单个空格字符。</p>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx23">
|
|
<td>
|
|
<p>在组成预处理记号而吸收字符时(即不组成注释或其他形式的空白),通用字符名会被识别并被<a href="cpp-language-charset.html#.E7.BF.BB.E8.AF.91.E5.AD.97.E7.AC.A6.E9.9B.86">翻译字符集</a>中的指定元素替换,除非正在匹配以下内容中的字符序列:</p>
|
|
<div class="t-li1"><span class="t-li">a)</span> <a href="cpp-language-character_literal.html">字符字面量</a>(<span class="t-spar">c字符序列</span>)</div>
|
|
<div class="t-li1"><span class="t-li">b)</span> <a href="cpp-language-string_literal.html">字符串字面量</a>(<span class="t-spar">s字符序列</span> 和 <span class="t-spar">r字符序列</span>),但不包括分隔符(<span class="t-spar">d字符序列</span>)</div>
|
|
<div class="t-li1"><span class="t-li">c)</span> <a href="cpp-preprocessor-include.html">要包含的文件名</a>(<span class="t-spar">h字符序列</span> 和 <span class="t-spar">q字符序列</span>)</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx23">(C++23 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<p><span id=".E6.9C.80.E5.A4.A7.E5.90.9E.E5.99.AC"></span> 如果一个给定字符前的输入已被解析为预处理记号,下一个预处理记号通常会由能构成预处理记号的最长字符序列构成,即使这样处理会导致后续分析失败。这常被称为<i>最大吞噬</i>。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw4">int</span> foo <span class="sy1">=</span> <span class="nu0">1</span><span class="sy4">;</span>
|
|
<span class="kw4">int</span> bar <span class="sy1">=</span> <span class="nu12">0xE</span><span class="sy2">+</span>foo<span class="sy4">;</span> <span class="co1">// 错误:非法的预处理数字 0xE+foo</span>
|
|
<span class="kw4">int</span> baz <span class="sy1">=</span> <span class="nu12">0xE</span> <span class="sy2">+</span> foo<span class="sy4">;</span> <span class="co1">// OK</span>
|
|
|
|
<span class="kw4">int</span> quux <span class="sy1">=</span> bar<span class="sy2">+++++</span>baz<span class="sy4">;</span> <span class="co1">// 错误:bar++ ++ +baz,而非 bar++ + ++baz。</span></pre></div>
|
|
</div>
|
|
<p>最大吞噬规则<span class="t-rev-inl t-until-cxx11"><span>只</span> <span><span class="t-mark-rev t-until-cxx11">(C++11 前)</span></span></span>有以下例外:</p>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<ul>
|
|
<li>如果以下一个字符开头的字符序列可作为<a href="cpp-language-string_literal.html">原始字符串字面量</a>的前缀和起始双引号,那么下个预处理记号应当为原始字符串字面量。该字面量由匹配原始字符串模式的最短字符序列组成。</li>
|
|
</ul>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="co2">#define R "x"</span>
|
|
<span class="kw4">const</span> <span class="kw4">char</span><span class="sy2">*</span> s <span class="sy1">=</span> R<span class="st0">"y"</span><span class="sy4">;</span> <span class="co1">// 非良构的原始字符串字面量,而非 "x" "y"</span>
|
|
<span class="kw4">const</span> <span class="kw4">char</span><span class="sy2">*</span> s2 <span class="sy1">=</span> R<span class="st0">"(a)"</span> <span class="st0">"b)"</span><span class="sy4">;</span> <span class="co1">// 原始字符串字面量后随普通字符串字面量</span></pre></div>
|
|
</div>
|
|
<ul>
|
|
<li>如果接下来的三个字符是 <code><b><::</b></code>且后继字符不是 <code><b>:</b></code> 或者 <code><b>></b></code>,那么把 <code><b><</b></code> 自身当做预处理记号(而非<a href="cpp-language-operator_alternative.html">代用记号</a> <code><:</code> 的首字符)。</li>
|
|
</ul>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">struct</span> Foo <span class="br0">{</span> <span class="kw4">static</span> <span class="kw4">const</span> <span class="kw4">int</span> v <span class="sy1">=</span> <span class="nu0">1</span><span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
<a href="cpp-container-vector.html"><span class="kw1269">std::<span class="me2">vector</span></span></a><span class="sy1"><</span><span class="sy4">::</span><span class="me2">Foo</span><span class="sy1">></span> x<span class="sy4">;</span> <span class="co1">// OK,<: 未被当作 [ 的代用记号</span>
|
|
<span class="kw4">extern</span> <span class="kw4">int</span> y<span class="sy1"><</span><span class="sy4">::</span><span class="sy1">></span><span class="sy4">;</span> <span class="co1">// OK,同 extern int y[]。</span>
|
|
<span class="kw4">int</span> z<span class="sy1"><</span><span class="sy4">:::</span><span class="me2">Foo</span><span class="sy4">::</span><span class="me2">value</span><span class="sy4">:</span><span class="sy1">></span><span class="sy4">;</span> <span class="co1">// OK,int z[::Foo::value];</span></pre></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<ul>
|
|
<li>头文件名预处理记号只会在 <code>#include</code> 指令中形成。</li>
|
|
</ul>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><a href="cpp-container-vector.html"><span class="kw1269">std::<span class="me2">vector</span></span></a><span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span> x<span class="sy4">;</span> <span class="co1">// OK,<int> 不是头文件名</span></pre></div>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E9.98.B6.E6.AE.B5_4">阶段 4</span></h3>
|
|
<div class="t-li1"><span class="t-li">1)</span> 执行<a href="cpp-preprocessor.html">预处理器</a>。<span class="t-rev-inl t-until-cxx23"><span>如果<a href="cpp-preprocessor-replace.html#.23_and_.23.23_operators">记号拼接</a>产生<a href="cpp-language-escape.html">通用字符名</a>,那么行为未定义。</span> <span><span class="t-mark-rev t-until-cxx23">(C++23 前)</span></span></span></div>
|
|
<div class="t-li1"><span class="t-li">2)</span> <a href="cpp-preprocessor-include.html">#include</a> 指令所引入的每个文件都经历阶段 1 到 4 的处理,递归执行。</div>
|
|
<div class="t-li1"><span class="t-li">3)</span> 此阶段结束时,所有预处理器指令都应从源(代码)移除。</div>
|
|
<h3><span class="mw-headline" id=".E9.98.B6.E6.AE.B5_5">阶段 5</span></h3>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-until-cxx23">
|
|
<td>
|
|
<div class="t-li1"><span class="t-li">1)</span> 将<a href="cpp-language-character_literal.html">字符字面量</a>及<a href="cpp-language-string_literal.html">字符串字面量</a>中的所有字符从源字符集转换到<i>执行字符集</i>(可以是 UTF-8 这样的多字节字符集,只要<a href="cpp-language-charset.html#.E5.9F.BA.E7.A1.80.E6.BA.90.E5.AD.97.E7.AC.A6.E9.9B.86">基础源字符集</a>的 96 个字符都拥有单字节表示即可)。</div>
|
|
<div class="t-li1"><span class="t-li">2)</span> 将字符字面量和非原始字符串字面量中的<a href="cpp-language-escape.html">转义序列</a>和通用字符名展开,并转换到<i>执行字符集</i>。 若某个通用字符名所指定的字符不是执行字符集的成员,则结果是由实现定义的,但保证不是空(宽)字符。</div>
|
|
<p>注意:某些实现能以命令行选项控制此阶段所进行的转换:gcc 和 clang 用 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy2">-</span>finput<span class="sy2">-</span>charset</span></span> 指定源字符集的编码,用 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy2">-</span>fexec<span class="sy2">-</span>charset</span></span> 和 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy2">-</span>fwide<span class="sy2">-</span>exec<span class="sy2">-</span>charset</span></span> 指定<span class="t-rev-inl t-since-cxx11"><span>无编码前缀的</span> <span><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></span></span>字符串和字符字面量中的执行字符集的编码,而 Visual Studio 2015 Update 2 及之后版本分别用 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy2">/</span>source<span class="sy2">-</span>charset</span></span> 和 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy2">/</span>execution<span class="sy2">-</span>charset</span></span> 指定源字符集和执行字符集。</p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-until-cxx23">(C++23 前)</span></td>
|
|
</tr>
|
|
<tr class="t-rev t-since-cxx23">
|
|
<td>
|
|
<p>对于每个含有多个相邻<a href="cpp-language-string_literal.html">字符串字面量</a>记号的序列,都会有一个以<a href="cpp-language-string_literal.html#.E6.8B.BC.E6.8E.A5">此规则</a>指定的共同编码前缀。其中每个字符串字面量记号都会被视为拥有该共同编码前缀。 (字符转换改为在阶段 3 执行)</p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx23">(C++23 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<h3><span class="mw-headline" id=".E9.98.B6.E6.AE.B5_6">阶段 6</span></h3>
|
|
<p>拼接相邻的<a href="cpp-language-string_literal.html">字符串字面量</a>。</p>
|
|
<h3><span class="mw-headline" id=".E9.98.B6.E6.AE.B5_7">阶段 7</span></h3>
|
|
<p>进行编译:将各个预处理记号转换成记号。将所有记号当作一个翻译单元进行语法和语义分析并进行翻译。</p>
|
|
<h3><span class="mw-headline" id=".E9.98.B6.E6.AE.B5_8">阶段 8</span></h3>
|
|
<p>检验每个翻译单元,产生所要求的模板实例化的列表,其中包括<a href="cpp-language-class_template.html">显式实例化</a>所要求的实例化。定位模板定义,并进行所要求的实例化,以产生<i>实例化单元</i>。</p>
|
|
<h3><span class="mw-headline" id=".E9.98.B6.E6.AE.B5_9">阶段 9</span></h3>
|
|
<p>将翻译单元、实例化单元和为满足外部引用所需的库组件汇集成一个程序映像,它含有在其执行环境中执行所需的信息。</p>
|
|
<h3><span class="mw-headline" id=".E6.B3.A8.E8.A7.A3">注解</span></h3>
|
|
<p>某些编译器不实现实例化单元(又称为<a rel="nofollow" class="external text" href="http://docs.oracle.com/cd/E18659_01/html/821-1383/bkagr.html#scrolltoc">模板仓库</a>或<a rel="nofollow" class="external text" href="http://www-01.ibm.com/support/knowledgecenter/SSXVZZ_12.1.0/com.ibm.xlcpp121.linux.doc/compiler_ref/fcat_template.html?lang=en">模板注册表</a>),而是简单地在阶段 7 编译每个模板实例化,存储代码于其所显式或隐式要求的对象文件中,然后由连接器于阶段 9 将这些编译后的实例化缩减到一个。</p>
|
|
<h3><span class="mw-headline" id=".E5.BC.95.E7.94.A8">引用</span></h3>
|
|
<div class="t-ref-std-c++20">
|
|
<ul>
|
|
<li>C++20 标准(ISO/IEC 14882:2020):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>5.2 Phases of translation [lex.phases]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
<div class="t-ref-std-c++17">
|
|
<ul>
|
|
<li>C++17 标准(ISO/IEC 14882:2017):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>5.2 Phases of translation [lex.phases]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
<div class="t-ref-std-c++14">
|
|
<ul>
|
|
<li>C++14 标准(ISO/IEC 14882:2014):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>2.2 Phases of translation [lex.phases]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
<div class="t-ref-std-c++11">
|
|
<ul>
|
|
<li>C++11 标准(ISO/IEC 14882:2011):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>2.2 Phases of translation [lex.phases]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
<div class="t-ref-std-c++03">
|
|
<ul>
|
|
<li>C++03 标准(ISO/IEC 14882:2003):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>2.1 Phases of translation [lex.phases]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
<div class="t-ref-std-c++98">
|
|
<ul>
|
|
<li>C++98 标准(ISO/IEC 14882:1998):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>2.1 Phases of translation [lex.phases]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E5.8F.82.E9.98.85">参阅</span></h3>
|
|
<table class="t-dsc-begin"></table>
|
|
</div>
|
|
<div class="visualClear"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|