271 lines
24 KiB
HTML
271 lines
24 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-c_language_behavior 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="c.html">c</a>‎ | <a href="c-language.html">language</a></span></div>
|
|
<div id="mw-content-text" lang="zh" dir="ltr" class="mw-content-ltr" xml:lang="zh">
|
|
<p>C 语言标准精确指定了 C 语言程序的<a href="https://zh.cppreference.com/mwiki/index.php?title=c/language/as_if&action=edit&redlink=1" class="new" title="c/language/as if(页面不存在)">可观察行为</a>,除了下列分类之一:</p>
|
|
<ul>
|
|
<li><i>未定义行为</i> - 程序的该行为没有限制。未定义行为的例子是越过数组边界的访问、有符号整数溢出、空指针解引用、在表达式中<a href="c-language-eval_order.html">超过一次</a>修改标量而其中无顺序点、通过不同类型的指针访问对象,等等。编译器不要求诊断未定义行为(尽管多数简单情形是得到诊断的),且编译后的程序不要求做任何有意义的事。</li>
|
|
</ul>
|
|
<ul>
|
|
<li><i>未指定行为</i> - 容许二种或多种行为,且不要求实现规范每种行为。例如,<a href="c-language-eval_order.html">求值顺序</a>、同样的<a href="c-language-string_literal.html">字符串字面量</a>是否有别,等。每个未指定行为导致一组合法结果之一,并且可以在同一程序中重复时产生不同结果。</li>
|
|
</ul>
|
|
<ul>
|
|
<li><i>实现定义行为</i> - 在未指定行为之上,实现规范了如何选择。例如,字节中的位数,或有符号整数右移是算术还是逻辑。</li>
|
|
</ul>
|
|
<ul>
|
|
<li><i>本地环境限定行为</i> - 依赖于<a href="c-locale-setlocale.html">当前选择的本地环境</a>的实现定义行为。例如, <span class="t-lc"><a href="c-string-byte-islower.html">islower</a></span> 对任何 26 个小写拉丁字母外的字符是否返回 true。</li>
|
|
</ul>
|
|
<p>(注意:<a href="c-language-conformance.html">严格遵从</a>的程序不依赖任何未指定、未定义或实现定义行为)</p>
|
|
<p>要求编译器对违背任何 C 语法规则或语义约束的任何程序发布诊断消息(错误或警告),即使其行为被指定为未定义或实现定义,或者编译器可提供语言扩展以允许此种程序被接受。另外,不要求对未定义行为诊断。</p>
|
|
<h3><span class="mw-headline" id="UB_.E4.B8.8E.E4.BC.98.E5.8C.96">UB 与优化</span></h3>
|
|
<p>因为正确的 C 程序是没有未定义行为的,编译器可以在启用优化的条件下编译确实有 UB 的程序时,生成不期待的结果:</p>
|
|
<p>例如,</p>
|
|
<h4><span class="mw-headline" id=".E6.9C.89.E7.AC.A6.E5.8F.B7.E6.BA.A2.E5.87.BA">有符号溢出</span></h4>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="c source-c">
|
|
<pre class="de1"><span class="kw4">int</span> foo<span class="br0">(</span><span class="kw4">int</span> x<span class="br0">)</span> <span class="br0">{</span>
|
|
<span class="kw1">return</span> x<span class="sy2">+</span><span class="nu0">1</span> <span class="sy1">></span> x<span class="sy4">;</span> <span class="co1">// 真或为有符号溢出导致的 UB</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>可以编译成(<a rel="nofollow" class="external text" href="https://godbolt.org/z/D249FL">演示</a>)</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="c source-c">
|
|
<pre class="de1">foo<span class="sy4">:</span>
|
|
movl $<span class="nu19">1</span>, <span class="sy2">%</span>eax
|
|
ret</pre></div>
|
|
</div>
|
|
<h4><span class="mw-headline" id=".E8.B6.8A.E7.95.8C.E8.AE.BF.E9.97.AE">越界访问</span></h4>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="c source-c">
|
|
<pre class="de1"><span class="kw4">int</span> table<span class="br0">[</span><span class="nu0">4</span><span class="br0">]</span> <span class="sy1">=</span> <span class="br0">{</span><span class="nu0">0</span><span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw4">int</span> exists_in_table<span class="br0">(</span><span class="kw4">int</span> v<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="co1">// 在最初的 4 个迭代中返回真或因为越界访问 UB</span>
|
|
<span class="kw1">for</span> <span class="br0">(</span><span class="kw4">int</span> i <span class="sy1">=</span> <span class="nu0">0</span><span class="sy4">;</span> i <span class="sy1"><=</span> <span class="nu0">4</span><span class="sy4">;</span> i<span class="sy2">++</span><span class="br0">)</span> <span class="br0">{</span>
|
|
<span class="kw1">if</span> <span class="br0">(</span>table<span class="br0">[</span>i<span class="br0">]</span> <span class="sy1">==</span> v<span class="br0">)</span> <span class="kw1">return</span> <span class="nu0">1</span><span class="sy4">;</span>
|
|
<span class="br0">}</span>
|
|
<span class="kw1">return</span> <span class="nu0">0</span><span class="sy4">;</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>可以编译成(<a rel="nofollow" class="external text" href="https://godbolt.org/z/qoD2uP">演示</a>)</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="c source-c">
|
|
<pre class="de1">exists_in_table<span class="sy4">:</span>
|
|
movl $<span class="nu19">1</span>, <span class="sy2">%</span>eax
|
|
ret</pre></div>
|
|
</div>
|
|
<h4><span class="mw-headline" id=".E6.9C.AA.E5.88.9D.E5.A7.8B.E5.8C.96.E6.A0.87.E9.87.8F">未初始化标量</span></h4>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="c source-c">
|
|
<pre class="de1">_Bool p<span class="sy4">;</span> <span class="co1">// 未初始化局部变量</span>
|
|
<span class="kw1">if</span><span class="br0">(</span>p<span class="br0">)</span> <span class="co1">// 访问未初始化标量是 UB</span>
|
|
<a href="c-io-puts.html"><span class="kw836">puts</span></a><span class="br0">(</span><span class="st0">"p is true"</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw1">if</span><span class="br0">(</span><span class="sy3">!</span>p<span class="br0">)</span> <span class="co1">// 访问未初始化标量是 UB</span>
|
|
<a href="c-io-puts.html"><span class="kw836">puts</span></a><span class="br0">(</span><span class="st0">"p is false"</span><span class="br0">)</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
<p>可能产生下列输出(可在一个旧版本 gcc 观察到):</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="c source-c">
|
|
<pre class="de1">p is <span class="kw2">true</span>
|
|
p is <span class="kw2">false</span></pre></div>
|
|
</div>
|
|
<div class="t-example">
|
|
<div class="t-example-live-link"></div>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="c source-c">
|
|
<pre class="de1"><a href="c-types-size_t.html"><span class="kw100">size_t</span></a> f<span class="br0">(</span><span class="kw4">int</span> x<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<a href="c-types-size_t.html"><span class="kw100">size_t</span></a> a<span class="sy4">;</span>
|
|
<span class="kw1">if</span><span class="br0">(</span>x<span class="br0">)</span> <span class="co1">// x为非零或UB</span>
|
|
a <span class="sy1">=</span> <span class="nu0">42</span><span class="sy4">;</span>
|
|
<span class="kw1">return</span> a<span class="sy4">;</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<p>可以编译成(<a rel="nofollow" class="external text" href="https://godbolt.org/z/u9lBlY">演示</a>)</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="c source-c">
|
|
<pre class="de1">f<span class="sy4">:</span>
|
|
mov eax, <span class="nu0">42</span>
|
|
ret</pre></div>
|
|
</div>
|
|
<h4><span class="mw-headline" id=".E9.9D.9E.E6.B3.95.E6.A0.87.E9.87.8F">非法标量</span></h4>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="c source-c">
|
|
<pre class="de1"><span class="kw4">int</span> f<span class="br0">(</span><span class="kw4">void</span><span class="br0">)</span> <span class="br0">{</span>
|
|
_Bool b <span class="sy1">=</span> <span class="nu0">0</span><span class="sy4">;</span>
|
|
<span class="kw4">unsigned</span> <span class="kw4">char</span><span class="sy2">*</span> p <span class="sy1">=</span><span class="br0">(</span><span class="kw4">unsigned</span> <span class="kw4">char</span><span class="sy2">*</span><span class="br0">)</span><span class="sy3">&</span>b<span class="sy4">;</span>
|
|
<span class="sy2">*</span>p <span class="sy1">=</span> <span class="nu0">10</span><span class="sy4">;</span>
|
|
<span class="co1">// 从 b 读取现在是 UB</span>
|
|
<span class="kw1">return</span> b <span class="sy1">==</span> <span class="nu0">0</span><span class="sy4">;</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>可编译成(<a rel="nofollow" class="external text" href="https://gcc.godbolt.org/z/JGG6uI">演示</a>)</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="c source-c">
|
|
<pre class="de1">f<span class="br0">(</span><span class="br0">)</span><span class="sy4">:</span>
|
|
movl $<span class="nu19">11</span>, <span class="sy2">%</span>eax
|
|
ret</pre></div>
|
|
</div>
|
|
<h4><span class="mw-headline" id=".E7.A9.BA.E6.8C.87.E9.92.88.E8.A7.A3.E5.BC.95.E7.94.A8">空指针解引用</span></h4>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="c source-c">
|
|
<pre class="de1"><span class="kw4">int</span> foo<span class="br0">(</span><span class="kw4">int</span><span class="sy2">*</span> p<span class="br0">)</span> <span class="br0">{</span>
|
|
<span class="kw4">int</span> x <span class="sy1">=</span> <span class="sy2">*</span>p<span class="sy4">;</span>
|
|
<span class="kw1">if</span><span class="br0">(</span><span class="sy3">!</span>p<span class="br0">)</span> <span class="kw1">return</span> x<span class="sy4">;</span> <span class="co1">// 为上述 UB ,或绝不采用此分支</span>
|
|
<span class="kw1">else</span> <span class="kw1">return</span> <span class="nu0">0</span><span class="sy4">;</span>
|
|
<span class="br0">}</span>
|
|
<span class="kw4">int</span> bar<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
|
|
<span class="kw4">int</span><span class="sy2">*</span> p <span class="sy1">=</span> <a href="c-types-NULL.html"><span class="kw103">NULL</span></a><span class="sy4">;</span>
|
|
<span class="kw1">return</span> <span class="sy2">*</span>p<span class="sy4">;</span> <span class="co1">// 无条件 UB</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>可以编译成( <a rel="nofollow" class="external text" href="https://godbolt.org/z/W6nz9j">foo 用 gcc , bar 用 clang</a> )</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="c source-c">
|
|
<pre class="de1">foo<span class="sy4">:</span>
|
|
xorl <span class="sy2">%</span>eax, <span class="sy2">%</span>eax
|
|
ret
|
|
bar<span class="sy4">:</span>
|
|
retq</pre></div>
|
|
</div>
|
|
<h4><span class="mw-headline" id=".E8.AE.BF.E9.97.AE.E4.BC.A0.E9.80.92.E7.BB.99_realloc_.E7.9A.84.E6.8C.87.E9.92.88">访问传递给 realloc 的指针</span></h4>
|
|
<div class="t-example">
|
|
<p>选择 Clang 以观察示出的输出</p>
|
|
<div class="t-example-live-link"></div>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="c source-c">
|
|
<pre class="de1"><span class="co2">#include <stdio.h></span>
|
|
<span class="co2">#include <stdlib.h></span>
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="kw4">void</span><span class="br0">)</span> <span class="br0">{</span>
|
|
<span class="kw4">int</span> <span class="sy2">*</span>p <span class="sy1">=</span> <span class="br0">(</span><span class="kw4">int</span><span class="sy2">*</span><span class="br0">)</span><a href="c-memory-malloc.html"><span class="kw403">malloc</span></a><span class="br0">(</span><span class="kw4">sizeof</span><span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw4">int</span> <span class="sy2">*</span>q <span class="sy1">=</span> <span class="br0">(</span><span class="kw4">int</span><span class="sy2">*</span><span class="br0">)</span><a href="c-memory-realloc.html"><span class="kw405">realloc</span></a><span class="br0">(</span>p, <span class="kw4">sizeof</span><span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="sy2">*</span>p <span class="sy1">=</span> <span class="nu0">1</span><span class="sy4">;</span> <span class="co1">// 访问传递给 realloc 的指针是 UB</span>
|
|
<span class="sy2">*</span>q <span class="sy1">=</span> <span class="nu0">2</span><span class="sy4">;</span>
|
|
<span class="kw1">if</span> <span class="br0">(</span>p <span class="sy1">==</span> q<span class="br0">)</span> <span class="co1">// 访问传递给 realloc 的指针是 UB</span>
|
|
<a href="c-io-fprintf.html"><span class="kw851">printf</span></a><span class="br0">(</span><span class="st0">"%d%d<span class="es1">\n</span>"</span>, <span class="sy2">*</span>p, <span class="sy2">*</span>q<span class="br0">)</span><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">12</pre></div>
|
|
</div>
|
|
</div>
|
|
<h4><span class="mw-headline" id=".E6.97.A0.E5.89.AF.E6.95.88.E5.BA.94.E7.9A.84.E6.97.A0.E9.99.90.E5.BE.AA.E7.8E.AF">无副效应的无限循环</span></h4>
|
|
<div class="t-example">
|
|
<p>选择 Clang 以观察示出的输出</p>
|
|
<div class="t-example-live-link"></div>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="c source-c">
|
|
<pre class="de1"><span class="co2">#include <stdio.h></span>
|
|
|
|
<span class="kw4">int</span> fermat<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
|
|
<span class="kw4">const</span> <span class="kw4">int</span> MAX <span class="sy1">=</span> <span class="nu0">1000</span><span class="sy4">;</span>
|
|
<span class="kw4">int</span> a<span class="sy1">=</span><span class="nu0">1</span>,b<span class="sy1">=</span><span class="nu0">1</span>,c<span class="sy1">=</span><span class="nu0">1</span><span class="sy4">;</span>
|
|
<span class="co1">// 无副效应的无限循环是 UB</span>
|
|
<span class="kw1">while</span> <span class="br0">(</span><span class="nu0">1</span><span class="br0">)</span> <span class="br0">{</span>
|
|
<span class="kw1">if</span> <span class="br0">(</span><span class="br0">(</span><span class="br0">(</span>a<span class="sy2">*</span>a<span class="sy2">*</span>a<span class="br0">)</span> <span class="sy1">==</span> <span class="br0">(</span><span class="br0">(</span>b<span class="sy2">*</span>b<span class="sy2">*</span>b<span class="br0">)</span><span class="sy2">+</span><span class="br0">(</span>c<span class="sy2">*</span>c<span class="sy2">*</span>c<span class="br0">)</span><span class="br0">)</span><span class="br0">)</span><span class="br0">)</span> <span class="kw1">return</span> <span class="nu0">1</span><span class="sy4">;</span>
|
|
a<span class="sy2">++</span><span class="sy4">;</span>
|
|
<span class="kw1">if</span> <span class="br0">(</span>a<span class="sy1">></span>MAX<span class="br0">)</span> <span class="br0">{</span> a<span class="sy1">=</span><span class="nu0">1</span><span class="sy4">;</span> b<span class="sy2">++</span><span class="sy4">;</span> <span class="br0">}</span>
|
|
<span class="kw1">if</span> <span class="br0">(</span>b<span class="sy1">></span>MAX<span class="br0">)</span> <span class="br0">{</span> b<span class="sy1">=</span><span class="nu0">1</span><span class="sy4">;</span> c<span class="sy2">++</span><span class="sy4">;</span> <span class="br0">}</span>
|
|
<span class="kw1">if</span> <span class="br0">(</span>c<span class="sy1">></span>MAX<span class="br0">)</span> <span class="br0">{</span> c<span class="sy1">=</span><span class="nu0">1</span><span class="sy4">;</span><span class="br0">}</span>
|
|
<span class="br0">}</span>
|
|
<span class="kw1">return</span> <span class="nu0">0</span><span class="sy4">;</span>
|
|
<span class="br0">}</span>
|
|
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="kw4">void</span><span class="br0">)</span> <span class="br0">{</span>
|
|
<span class="kw1">if</span> <span class="br0">(</span>fermat<span class="br0">(</span><span class="br0">)</span><span class="br0">)</span>
|
|
<a href="c-io-puts.html"><span class="kw836">puts</span></a><span class="br0">(</span><span class="st0">"Fermat's Last Theorem has been disproved."</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw1">else</span>
|
|
<a href="c-io-puts.html"><span class="kw836">puts</span></a><span class="br0">(</span><span class="st0">"Fermat's Last Theorem has not been disproved."</span><span class="br0">)</span><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">Fermat's Last Theorem has been disproved.</pre></div>
|
|
</div>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E5.BC.95.E7.94.A8">引用</span></h3>
|
|
<div class="t-ref-std-11">
|
|
<ul>
|
|
<li>C11 标准(ISO/IEC 9899:2011):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>3.4 Behavior (p: 3-4)</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>4/2 Undefined behavior (p: 8)</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<div class="t-ref-std-c99">
|
|
<ul>
|
|
<li>C99 标准(ISO/IEC 9899:1999):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>3.4 Behavior (p: 3-4)</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>4/2 Undefined behavior (p: 7)</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<div class="t-ref-std-c89">
|
|
<ul>
|
|
<li>C89/C90 标准(ISO/IEC 9899:1990):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>1.6 DEFINITIONS OF TERMS</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E5.A4.96.E9.83.A8.E9.93.BE.E6.8E.A5">外部链接</span></h3>
|
|
<ul>
|
|
<li><a rel="nofollow" class="external text" href="http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html">每个 C 程序员都该知道关于未定义行为的事 #1/3</a></li>
|
|
<li><a rel="nofollow" class="external text" href="http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_14.html">每个 C 程序员都该知道关于未定义行为的事 #2/3</a></li>
|
|
<li><a rel="nofollow" class="external text" href="http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_21.html">每个 C 程序员都该知道关于未定义行为的事 #3/3</a></li>
|
|
<li><a rel="nofollow" class="external text" href="http://blogs.msdn.com/b/oldnewthing/archive/2014/06/27/10537746.aspx">未定义行为能导致时间旅行(在所有事项中,时间旅行可是最惊人的)</a></li>
|
|
<li><a rel="nofollow" class="external text" href="http://www.cs.utah.edu/~regehr/papers/overflow12.pdf">了解 C/C++ 中的整数溢出</a></li>
|
|
<li><a rel="nofollow" class="external text" href="http://kukuruku.co/hub/cpp/undefined-behavior-and-fermats-last-theorem">未定义行为及费马最后定理</a></li>
|
|
<li><a rel="nofollow" class="external text" href="http://lwn.net/Articles/342330/">空指针的趣事, part 1</a> ( Linux 2.6.30 中由空指针解引用导致的未定义行为所引发的局部滥用)</li>
|
|
</ul>
|
|
<h3><span class="mw-headline" id=".E5.8F.82.E9.98.85">参阅</span></h3>
|
|
<table class="t-dsc-begin"></table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="visualClear"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|