上传cppreference
This commit is contained in:
135
web/cppreference-zh/chmhelp/cpp-language-as_if.html
Normal file
135
web/cppreference-zh/chmhelp/cpp-language-as_if.html
Normal file
@@ -0,0 +1,135 @@
|
||||
<!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_as_if 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>允许进行任何不改变程序可观察行为的代码转换。</p>
|
||||
<h3><span class="mw-headline" id=".E8.A7.A3.E9.87.8A">解释</span></h3>
|
||||
<p>只要保证以下几点,就允许 C++ 编译器对程序进行任何修改:</p>
|
||||
<table class="t-rev-begin">
|
||||
<tr class="t-rev t-until-cxx11">
|
||||
<td>
|
||||
<div class="t-li1"><span class="t-li">1)</span> 在每一个<a href="cpp-language-eval_order.html">序列点</a>, 所有 <a href="cpp-language-cv.html">volatile</a> 对象的值是稳定的(之前的求值已经完成,新的求值没有开始)</div>
|
||||
</td>
|
||||
<td><span class="t-mark-rev t-until-cxx11">(C++11 前)</span></td>
|
||||
</tr>
|
||||
<tr class="t-rev t-since-cxx11">
|
||||
<td>
|
||||
<div class="t-li1"><span class="t-li">1)</span> 对 <a href="cpp-language-cv.html">volatile</a> 对象的访问(读或写)严格按照它们所发生的表达式的语义进行。特别地,它们和同一线程中的其他 volatile 访问之间<a href="cpp-atomic-memory_order.html">不会发生重排</a>。</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">2)</span> 程序终止时,写入文件的数据完全如同程序是按照所写的代码那样执行的一般。</div>
|
||||
<div class="t-li1"><span class="t-li">3)</span> 发送到交互式设备的提示文本将在程序等待输入之前显示出来。</div>
|
||||
<div class="t-li1"><span class="t-li">4)</span> 如果 ISO C 编译指示 <a href="cpp-preprocessor-impl.html"><tt>#pragma STDC FENV_ACCESS</tt></a> 受到支持并被设为 <code>ON</code>,则保证浮点算术运算符和函数调用会观察到对<a href="cpp-numeric-fenv.html">浮点环境</a>(浮点异常和舍入模式)的修改,就如同按照所写的代码那样执行一般,除非
|
||||
<dl>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>除去转型和赋值以外的任何浮点表达式的结果可能有不同于表达式本身的浮点范围和精度(参见 <span class="t-lc"><a href="cpp-types-climits-FLT_EVAL_METHOD.html">FLT_EVAL_METHOD</a></span> )</li>
|
||||
<li>尽管如此,任何浮点表达式的中间结果可能按照无限的范围和精度进行计算(除非 <a href="cpp-preprocessor-impl.html"><tt>#pragma STDC FP_CONTRACT</tt></a> 为 <code>OFF</code> )</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<h3><span class="mw-headline" id=".E6.B3.A8.E9.87.8A">注释</span></h3>
|
||||
<p>由于编译器(通常)不能分析一个外部库的代码,以确定它是否执行 I/O 或者 volatile 访问,因此第三方库的调用同样不受这种优化的影响。然而,标准库调用可能会在优化过程中被其它调用替换,被消除,或者被添加到程序中。静态连接的第三方库代码可能会参与连接时优化。</p>
|
||||
<p>有<a href="cpp-language-ub.html">未定义行为</a>的程序,例如,越界的数组访问,修改 const 对象,违反<a href="cpp-language-eval_order.html">求值顺序</a>的规定等,不受“如同”规则的限制:当使用不同的优化设置重新编译时,它们常常会表现出不同的可观察行为。例如,如果一个有符号整数溢出的测试依赖于溢出的结果,比如 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw1">if</span><span class="br0">(</span>n<span class="sy2">+</span><span class="nu0">1</span> <span class="sy1"><</span> n<span class="br0">)</span> abort<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span></span></span> ,<a rel="nofollow" class="external text" href="http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_14.html">则它会被某些编译器完全删除</a>,因为<a href="cpp-language-operator_arithmetic.html#.E6.BA.A2.E5.87.BA">有符号数溢出是未定义行为</a>而优化器可以自由地假设它永远不会发生,从而测试是多余的。</p>
|
||||
<p><a href="cpp-language-copy_elision.html">复制消除</a>是“如同”规则的一项例外:编译器可以删除对移动和复制构造函数的调用以及关联的临时对象的析构函数的调用,纵使这些调用具有可观察的副作用也是如此。</p>
|
||||
<table class="t-rev-begin">
|
||||
<tr class="t-rev t-since-cxx14">
|
||||
<td>
|
||||
<p><a href="cpp-language-new.html#.E5.88.86.E9.85.8D">new 表达式</a>拥有“如同”规则的另一项例外:编译器可以删除对<a href="cpp-memory-new-operator_new.html">可替换分配函数</a>的调用,即使提供了一个用户定义的替代函数并且具有可观察的副作用也是如此。</p>
|
||||
</td>
|
||||
<td><span class="t-mark-rev t-since-cxx14">(C++14 起)</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>浮点异常的数目和顺序可以被优化改变,只要下一次浮点操作所观察到的状态就如同没有优化发生一样即可:</p>
|
||||
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
||||
<div class="cpp source-cpp">
|
||||
<pre class="de1"><span class="co2">#pragma STDC FENV_ACCESS ON</span>
|
||||
<span class="kw1">for</span> <span class="br0">(</span>i <span class="sy1">=</span> <span class="nu0">0</span><span class="sy4">;</span> i <span class="sy1"><</span> n<span class="sy4">;</span> i<span class="sy2">++</span><span class="br0">)</span> x <span class="sy2">+</span> <span class="nu0">1</span><span class="sy4">;</span> <span class="co1">// x+1 是死代码,但可能会导致浮点异常</span>
|
||||
<span class="co1">// (除非优化器能证否)。然而,执行它 n 次只会反复导致同样的异常。</span>
|
||||
<span class="co1">// 所以这可以被优化为:</span>
|
||||
<span class="kw1">if</span> <span class="br0">(</span><span class="nu0">0</span> <span class="sy1"><</span> n<span class="br0">)</span> x <span class="sy2">+</span> <span class="nu0">1</span><span class="sy4">;</span></pre></div>
|
||||
</div>
|
||||
<h3><span class="mw-headline" id=".E7.A4.BA.E4.BE.8B">示例</span></h3>
|
||||
<div class="t-example">
|
||||
<div class="t-example-live-link"></div>
|
||||
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
||||
<div class="cpp source-cpp">
|
||||
<pre class="de1"><span class="kw4">int</span><span class="sy3">&</span> preinc<span class="br0">(</span><span class="kw4">int</span><span class="sy3">&</span> n<span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> <span class="sy2">++</span>n<span class="sy4">;</span> <span class="br0">}</span>
|
||||
<span class="kw4">int</span> add<span class="br0">(</span><span class="kw4">int</span> n, <span class="kw4">int</span> m<span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> n<span class="sy2">+</span>m<span class="sy4">;</span> <span class="br0">}</span>
|
||||
|
||||
<span class="co1">// volatile 输入用来避免常量折叠</span>
|
||||
<span class="kw4">volatile</span> <span class="kw4">int</span> input <span class="sy1">=</span> <span class="nu0">7</span><span class="sy4">;</span>
|
||||
|
||||
<span class="co1">// volatile 输出用来确保结果为可观察副作用</span>
|
||||
<span class="kw4">volatile</span> <span class="kw4">int</span> result<span class="sy4">;</span>
|
||||
|
||||
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span>
|
||||
<span class="br0">{</span>
|
||||
<span class="kw4">int</span> n <span class="sy1">=</span> input<span class="sy4">;</span>
|
||||
<span class="co1">// 使用内置运算符会导致未定义行为</span>
|
||||
<span class="co1">// int m = ++n + ++n;</span>
|
||||
<span class="co1">// 但使用函数会确保代码的执行如同这些函数不发生重叠一般</span>
|
||||
<span class="kw4">int</span> m <span class="sy1">=</span> add<span class="br0">(</span>preinc<span class="br0">(</span>n<span class="br0">)</span>, preinc<span class="br0">(</span>n<span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span>
|
||||
result <span class="sy1">=</span> m<span class="sy4">;</span>
|
||||
<span class="br0">}</span></pre></div>
|
||||
</div>
|
||||
<p>输出:</p>
|
||||
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
||||
<div class="text source-text">
|
||||
<pre class="de1"># GCC 编译器所产生的 main() 函数的完整代码
|
||||
# x86 (Intel) 平台:
|
||||
movl input(%rip), %eax # eax = input
|
||||
leal 3(%rax,%rax), %eax # eax = 3 + eax + eax
|
||||
movl %eax, result(%rip) # result = eax
|
||||
xorl %eax, %eax # eax = 0 ( main() 的返回值)
|
||||
ret
|
||||
|
||||
# PowerPC (IBM) 平台:
|
||||
lwz 9,LC..1(2)
|
||||
li 3,0 # r3 = 0 ( main() 的返回值)
|
||||
lwz 11,0(9) # r11 = input;
|
||||
slwi 11,11,1 # r11 = r11 << 1;
|
||||
addi 0,11,3 # r0 = r11 + 3;
|
||||
stw 0,4(9) # result = r0;
|
||||
blr
|
||||
|
||||
# Sparc (Sun) 平台:
|
||||
sethi %hi(result), %g2
|
||||
sethi %hi(input), %g1
|
||||
mov 0, %o0 # o0 = 0 ( main() 的返回值)
|
||||
ld [%g1+%lo(input)], %g1 # g1 = input
|
||||
add %g1, %g1, %g1 # g1 = g1 + g1
|
||||
add %g1, 3, %g1 # g1 = 3 + g1
|
||||
st %g1, [%g2+%lo(result)] # result = g1
|
||||
jmp %o7+8
|
||||
nop
|
||||
|
||||
# 所有情况下 preinc() 的副作用都被消除,
|
||||
# 而且整个 main() 函数被简化到等价于 result = 2*input + 3;</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<h3><span class="mw-headline" id=".E5.8F.82.E9.98.85">参阅</span></h3>
|
||||
<ul>
|
||||
<li><a href="cpp-language-copy_elision.html">复制消除</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="visualClear"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user