301 lines
23 KiB
HTML
301 lines
23 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_bit_field 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>
|
|
<p>位域声明是使用下列声明符的<a href="cpp-language-data_members.html">类数据成员声明</a>:</p>
|
|
<table class="t-sdsc-begin">
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td><span class="t-spar">标识符</span><span class="t-mark">(可选)</span> <span class="t-spar">属性</span><span class="t-mark">(可选)</span> <code><b>:</b></code> <span class="t-spar">大小</span></td>
|
|
<td>(1)</td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td><span class="t-spar">标识符</span><span class="t-mark">(可选)</span> <span class="t-spar">属性</span><span class="t-mark">(可选)</span> <code><b>:</b></code> <span class="t-spar">大小</span> <span class="t-spar">花括号或等号初始化器</span></td>
|
|
<td>(2)</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
</table>
|
|
<p>位域的<i>类型</i>由<a href="cpp-language-declarations.html">声明语法</a>的 <span class="t-spar">声明说明符序列</span> 引入.</p>
|
|
<table class="t-par-begin">
|
|
<tr class="t-par">
|
|
<td><span class="t-spar">属性</span></td>
|
|
<td>-</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span> 可选的任意数量<a href="cpp-language-attributes.html">属性</a>的序列</td>
|
|
</tr>
|
|
<tr class="t-par">
|
|
<td><span class="t-spar">标识符</span></td>
|
|
<td>-</td>
|
|
<td>被声明的位域名。名字是可选的:无名位域引入指定数量的填充位</td>
|
|
</tr>
|
|
<tr class="t-par">
|
|
<td><span class="t-spar">大小</span></td>
|
|
<td>-</td>
|
|
<td>值大于或等于零的<a href="cpp-language-constant_expression.html#.E6.95.B4.E5.9E.8B.E5.B8.B8.E9.87.8F.E8.A1.A8.E8.BE.BE.E5.BC.8F">整型常量表达式</a>。大于零时,这是位域将占有的位数。零值只能用于无名位域并具有特殊含义:它指定类定义中的下个位域应始于分配单元的边界。</td>
|
|
</tr>
|
|
<tr class="t-par">
|
|
<td><span class="t-spar">花括号或等号初始化器</span></td>
|
|
<td>-</td>
|
|
<td>此位域所使用的<a href="cpp-language-data_members.html#.E6.88.90.E5.91.98.E5.88.9D.E5.A7.8B.E5.8C.96">默认成员初始化器</a></td>
|
|
</tr>
|
|
</table>
|
|
<h3><span class="mw-headline" id=".E8.A7.A3.E9.87.8A">解释</span></h3>
|
|
<p>位域的类型只能是整型或枚举类型。</p>
|
|
<p>位域不能是<a href="cpp-language-static.html">静态数据成员</a>。</p>
|
|
<p>没有位域<a href="cpp-language-value_category.html">纯右值</a>:左值到右值转换始终生成位域底层类型的对象。</p>
|
|
<p>位域中的位数设置其所能保有的值的极限:</p>
|
|
<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="co2">#include <iostream></span>
|
|
<span class="kw1">struct</span> S <span class="br0">{</span>
|
|
<span class="co1">// 三位的无符号位域,</span>
|
|
<span class="co1">// 允许的值为 0...7</span>
|
|
<span class="kw4">unsigned</span> <span class="kw4">int</span> b <span class="sy4">:</span> <span class="nu0">3</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
S s <span class="sy1">=</span> <span class="br0">{</span><span class="nu0">6</span><span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="sy2">++</span>s.<span class="me1">b</span><span class="sy4">;</span> <span class="co1">// 在位域中存储值 7</span>
|
|
<a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> s.<span class="me1">b</span> <span class="sy1"><<</span> <span class="st0">'<span class="es1">\n</span>'</span><span class="sy4">;</span>
|
|
<span class="sy2">++</span>s.<span class="me1">b</span><span class="sy4">;</span> <span class="co1">// 值 8 不适合此位域</span>
|
|
<a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> s.<span class="me1">b</span> <span class="sy1"><<</span> <span class="st0">'<span class="es1">\n</span>'</span><span class="sy4">;</span> <span class="co1">// 正式来说是由实现定义的行为,通常为 0</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">7
|
|
0</pre></div>
|
|
</div>
|
|
</div>
|
|
<p>多个相邻位域通常打包在一起(尽管此行为是实现定义的):</p>
|
|
<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="co2">#include <iostream></span>
|
|
<span class="kw1">struct</span> S <span class="br0">{</span>
|
|
<span class="co1">// 将通常占用 2 字节:</span>
|
|
<span class="co1">// 3 位:b1 的值</span>
|
|
<span class="co1">// 2 位:不使用</span>
|
|
<span class="co1">// 6 位:b2 的值</span>
|
|
<span class="co1">// 2 位:b3 的值</span>
|
|
<span class="co1">// 3 位:不使用</span>
|
|
<span class="kw4">unsigned</span> <span class="kw4">char</span> b1 <span class="sy4">:</span> <span class="nu0">3</span>, <span class="sy4">:</span> <span class="nu0">2</span>, b2 <span class="sy4">:</span> <span class="nu0">6</span>, b3 <span class="sy4">:</span> <span class="nu0">2</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> sizeof<span class="br0">(</span>S<span class="br0">)</span> <span class="sy1"><<</span> <span class="st0">'<span class="es1">\n</span>'</span><span class="sy4">;</span> <span class="co1">// 通常打印 2</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">2</pre></div>
|
|
</div>
|
|
</div>
|
|
<p>特殊的零大小无名位域可用于强行打破填充。它指定下个位域始于其分配单元的开始处:</p>
|
|
<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="co2">#include <iostream></span>
|
|
<span class="kw1">struct</span> S <span class="br0">{</span>
|
|
<span class="co1">// 通常将占用 2 字节:</span>
|
|
<span class="co1">// 3 位:b1 的值</span>
|
|
<span class="co1">// 5 位:不使用</span>
|
|
<span class="co1">// 6 位:b2 的值</span>
|
|
<span class="co1">// 2 位:b3 的值</span>
|
|
<span class="kw4">unsigned</span> <span class="kw4">char</span> b1 <span class="sy4">:</span> <span class="nu0">3</span><span class="sy4">;</span>
|
|
<span class="kw4">unsigned</span> <span class="kw4">char</span> <span class="sy4">:</span><span class="nu0">0</span><span class="sy4">;</span> <span class="co1">// 开始新字节</span>
|
|
<span class="kw4">unsigned</span> <span class="kw4">char</span> b2 <span class="sy4">:</span> <span class="nu0">6</span><span class="sy4">;</span>
|
|
<span class="kw4">unsigned</span> <span class="kw4">char</span> b3 <span class="sy4">:</span> <span class="nu0">2</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> sizeof<span class="br0">(</span>S<span class="br0">)</span> <span class="sy1"><<</span> <span class="st0">'<span class="es1">\n</span>'</span><span class="sy4">;</span> <span class="co1">// 通常打印 2</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">2</pre></div>
|
|
</div>
|
|
</div>
|
|
<p>如果位域所指定的大小大于其类型的大小,那么值由类型所限制:<span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="cpp-types-integer.html"><span class="kw120">std::<span class="me2">uint8_t</span></span></a> b <span class="sy4">:</span> <span class="nu0">1000</span><span class="sy4">;</span></span></span> 仍会保有 0 与 255 间的值。额外位成为不使用的填充位。</p>
|
|
<p>因为位域不一定始于一个字节的开始处,所以不能取位域的地址。指向位域的指针和非 const 引用是不可行的。从位域<a href="cpp-language-reference_initialization.html">初始化 const 引用</a>时,将创建一个临时量(其类型是位域的类型),并以该位域的值复制初始化,而引用绑定到该临时量。</p>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-until-cxx20">
|
|
<td>
|
|
<p>位域没有<a href="cpp-language-data_members.html#.E6.88.90.E5.91.98.E5.88.9D.E5.A7.8B.E5.8C.96">默认成员初始化器</a>:<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">int</span> b <span class="sy4">:</span> <span class="nu0">1</span> <span class="sy1">=</span> <span class="nu0">0</span><span class="sy4">;</span></span></span> 与 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">int</span> b <span class="sy4">:</span> <span class="nu0">1</span> <span class="br0">{</span><span class="nu0">0</span><span class="br0">}</span></span></span> 为非良构。</p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-until-cxx20">(C++20 前)</span></td>
|
|
</tr>
|
|
<tr class="t-rev t-since-cxx20">
|
|
<td>
|
|
<p><span id="Cpp20_.E4.BD.8D.E5.9F.9F.E7.9A.84.E9.BB.98.E8.AE.A4.E6.88.90.E5.91.98.E5.88.9D.E5.A7.8B.E5.8C.96.E5.99.A8"></span> 在位域大小与默认初始化器有歧义的情况下,选择组成合法大小的最长记号序列:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw4">int</span> a<span class="sy4">;</span>
|
|
<span class="kw4">const</span> <span class="kw4">int</span> b <span class="sy1">=</span> <span class="nu0">0</span><span class="sy4">;</span>
|
|
<span class="kw1">struct</span> S <span class="br0">{</span>
|
|
<span class="co1">// 简单情况</span>
|
|
<span class="kw4">int</span> x1 <span class="sy4">:</span> <span class="nu0">8</span> <span class="sy1">=</span> <span class="nu0">42</span><span class="sy4">;</span> <span class="co1">// OK:"= 42" 是花括号或等号初始化器</span>
|
|
<span class="kw4">int</span> x2 <span class="sy4">:</span> <span class="nu0">8</span> <span class="br0">{</span> <span class="nu0">42</span> <span class="br0">}</span><span class="sy4">;</span> <span class="co1">// OK:"{ 42 }" 是花括号或等号初始化器</span>
|
|
<span class="co1">// 歧义</span>
|
|
<span class="kw4">int</span> y1 <span class="sy4">:</span> <span class="kw2">true</span> <span class="sy4">?</span> <span class="nu0">8</span> <span class="sy4">:</span> a <span class="sy1">=</span> <span class="nu0">42</span><span class="sy4">;</span> <span class="co1">// OK:不存在花括号或等号初始化器</span>
|
|
<span class="kw4">int</span> y2 <span class="sy4">:</span> <span class="kw2">true</span> <span class="sy4">?</span> <span class="nu0">8</span> <span class="sy4">:</span> b <span class="sy1">=</span> <span class="nu0">42</span><span class="sy4">;</span> <span class="co1">// 错误:不能赋值给 const int</span>
|
|
<span class="kw4">int</span> y3 <span class="sy4">:</span> <span class="br0">(</span><span class="kw2">true</span> <span class="sy4">?</span> <span class="nu0">8</span> <span class="sy4">:</span> b<span class="br0">)</span> <span class="sy1">=</span> <span class="nu0">42</span><span class="sy4">;</span> <span class="co1">// OK:"= 42" 是花括号或等号初始化器</span>
|
|
<span class="kw4">int</span> z <span class="sy4">:</span> <span class="nu0">1</span> <span class="sy3">||</span> new <span class="kw4">int</span> <span class="br0">{</span> <span class="nu0">0</span> <span class="br0">}</span><span class="sy4">;</span> <span class="co1">// OK:不存在花括号或等号初始化器</span>
|
|
<span class="br0">}</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<h3><span class="mw-headline" id=".E6.B3.A8.E8.A7.A3">注解</span></h3>
|
|
<p>位域的下列性质<i>由实现定义</i>:</p>
|
|
<ul>
|
|
<li>以范围外的值对有符号位域进行赋值或初始化,或对有符号位域进行自增越过其范围,所产生的值。</li>
|
|
<li>任何关于类对象中位域的实际分配细节:</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>例如,在某些平台上,位域不跨过字节,其他平台上会跨过</li>
|
|
<li>还有,在某些平台上,位域从左到右打包,其他为从右到左</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<p>在 C 编程语言中,位域的宽度不能超过底层类型的宽度,并且未显式标为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">signed</span></span></span> 或 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">unsigned</span></span></span> 的 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">int</span></span></span> 位域为有符号或无符号是实现定义的。例如,C 中 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">int</span> b<span class="sy4">:</span><span class="nu0">3</span><span class="sy4">;</span></span></span> 可能有值域 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="nu0">0</span>..<span class="nu0">7</span></span></span> 或 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy2">-</span><span class="nu0">4</span>..<span class="nu0">3</span></span></span>,但 C++ 中只允许后者。</p>
|
|
<h3><span class="mw-headline" id=".E7.BC.BA.E9.99.B7.E6.8A.A5.E5.91.8A">缺陷报告</span></h3>
|
|
<p>下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。</p>
|
|
<table class="dsctable" style="font-size:0.8em">
|
|
<tr>
|
|
<th>DR</th>
|
|
<th>应用于</th>
|
|
<th>出版时的行为</th>
|
|
<th>正确行为</th>
|
|
</tr>
|
|
<tr>
|
|
<td><a rel="nofollow" class="external text" href="https://wg21.cmeerw.net/cwg/issue739">CWG 739</a></td>
|
|
<td>C++98</td>
|
|
<td>不显式地为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">signed</span></span></span> 或 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">unsigned</span></span></span> 的位域的符号性是实现定义的</td>
|
|
<td>与底层类型一致</td>
|
|
</tr>
|
|
</table>
|
|
<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>11.4.9 Bit-fields [class.bit]</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>12.2.4 Bit-fields [class.bit]</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>9.6 Bit-fields [class.bit]</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>9.6 Bit-fields [class.bit]</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>9.6 Bit-fields [class.bit]</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>9.6 Bit-fields [class.bit]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E5.8F.82.E9.98.85">参阅</span></h3>
|
|
<table class="t-dsc-begin">
|
|
<tr class="t-dsc">
|
|
<td>
|
|
<div class="t-dsc-member-div">
|
|
<div><a href="cpp-utility-bitset.html"><span class="t-lines"><span>bitset</span></span></a></div>
|
|
</div>
|
|
</td>
|
|
<td>实现常量长度的位数组<br />
|
|
<span class="t-mark">(类模板)</span></td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="visualClear"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|