C++ 中的联合和按位运算
Unions in c++ and bitwise operations
我在源代码中看到了如下结构
template<unsigned bitno, unsigned nbits = 1, typename T = u8>
struct RegBit
{
T data;
enum { mask = (1u << nbits) - 1u };
template<typename T2>
RegBit& operator=(T2 val)
{
data = (data & ~(mask << bitno)) | ((nbits > 1 ? val & mask : !!val) << bitno);
return *this;
}
operator unsigned() const { return (data >> bitno) & mask; }
};
union {
u8 raw;
RegBit<0> r0;
RegBit<1> r1;
RegBit<2> r2;
RegBit<3> r3;
RegBit<6> r6;
RegBit<7> r7;
} P;
第一次阅读后,我发现类型为 RegBit
的对象的 unsigned
转换将 return data
的位数 bitno + 1
=].
但是,我不明白 =
重载运算符是如何处理的。我的意思是我理解语法,但不理解按位运算的含义。
最后一件事,如果你 运行 代码并将值影响到 P.raw
,你会注意到 ∀ i ∈ [0;7], P.ri.data = P.raw
。
这怎么可能?
当然,代码做了它应该做的事情,即:∀ i ∈ [0;7],(unsigned)P.ri
是 P.raw
的第 (i+1) 位.
operator= 是如何工作的?
当您编写 P.r2 = 1;
时,将调用 r2 成员的赋值运算符。所以它会产生 P.r2.operator= (1);
的效果,其中 returns 引用 P.r2
。
让我们分析一下专用模板中的分配细节,其中 bitno=2
、nbits=1
和 T
为 u8
:
mask = (1u << nbits) - 1u
= (1 shifted by 1 bits, aka binary 10) - 1
= binary 1 (i.e. it's a binary number with the n lowest bits set)
让我们一步步分析完整的表达式。首先是左边部分:
mask << bitno ===> binary 100
~(mask << bitno) ===> binary 1..1011 (aka the bit bitno is set to 0, counting from least significant bit)
(data & ~(mask << bitno)) ===> the bit bitno is set to 0 in data (thanks to the bitwise &)
现在表达式的右边部分:
(nbits > 1 ? val & mask : !!val) is a conditional operator:
if nbits >1 is true, then it's val&mask, aka the n lowest bits of val
if not, then it's !!val, aka "not not val" which evalauates to 0 if val is 0 and 1 if val is not 0.
In our case, it's the second alternative so 0 or 1 depending on val.
((nbits > 1 ? val & mask : !!val) << bitno) then shifts the 0 or the 1 by 2 bits.
现在终于将所有这些结合起来:
data = (data & ~(mask << bitno)) | ((nbits > 1 ? val & mask : !!val) << bitno);
= (data with the bit bitno set to 0) ored with (val expressed on one bit in the bit bitno, counting from the least significant )
另外说明,作为位值 0 与位值 x 的或运算结果为位值 x,此表达式将位 bitno 设置为 val(将 val 作为 bool 处理)。
但是工会应该做什么?
联合在同一内存位置处理其所有成员(它们都是同一类型 u8
)。
那么根据您的预期输出结果如下:
P.raw=0;
P.r2=1;
P.r3=0;
P.r4=1;
cout << (int)P.raw <<endl;
编写您的代码片段的乐观主义者当然期望结果为 20(又名二进制 10100)。这可能在许多编译器上都是这样工作的。但实际上,根据标准,这是绝对不能保证的:
9.5/1: In a union, at most one of the non-static data members can be active at any time, that is, the value of at most one of the
non-static data members can be stored in a union at any time.
另外说明,如果您将某些内容存储在 r2 中,您不确定是否会在 r4 中找到相同的值。唯一可以肯定的是,如果你在 r2 中存储了一些东西并且没有在其他成员中存储任何其他东西,你将在 r2 中找到你存储在那里的东西。
工会的替代方案
如果需要保证便携性,可以考虑使用std::bitset
or standard bitfields。
我在源代码中看到了如下结构
template<unsigned bitno, unsigned nbits = 1, typename T = u8>
struct RegBit
{
T data;
enum { mask = (1u << nbits) - 1u };
template<typename T2>
RegBit& operator=(T2 val)
{
data = (data & ~(mask << bitno)) | ((nbits > 1 ? val & mask : !!val) << bitno);
return *this;
}
operator unsigned() const { return (data >> bitno) & mask; }
};
union {
u8 raw;
RegBit<0> r0;
RegBit<1> r1;
RegBit<2> r2;
RegBit<3> r3;
RegBit<6> r6;
RegBit<7> r7;
} P;
第一次阅读后,我发现类型为 RegBit
的对象的 unsigned
转换将 return data
的位数 bitno + 1
=].
但是,我不明白 =
重载运算符是如何处理的。我的意思是我理解语法,但不理解按位运算的含义。
最后一件事,如果你 运行 代码并将值影响到 P.raw
,你会注意到 ∀ i ∈ [0;7], P.ri.data = P.raw
。
这怎么可能?
当然,代码做了它应该做的事情,即:∀ i ∈ [0;7],(unsigned)P.ri
是 P.raw
的第 (i+1) 位.
operator= 是如何工作的?
当您编写 P.r2 = 1;
时,将调用 r2 成员的赋值运算符。所以它会产生 P.r2.operator= (1);
的效果,其中 returns 引用 P.r2
。
让我们分析一下专用模板中的分配细节,其中 bitno=2
、nbits=1
和 T
为 u8
:
mask = (1u << nbits) - 1u
= (1 shifted by 1 bits, aka binary 10) - 1
= binary 1 (i.e. it's a binary number with the n lowest bits set)
让我们一步步分析完整的表达式。首先是左边部分:
mask << bitno ===> binary 100
~(mask << bitno) ===> binary 1..1011 (aka the bit bitno is set to 0, counting from least significant bit)
(data & ~(mask << bitno)) ===> the bit bitno is set to 0 in data (thanks to the bitwise &)
现在表达式的右边部分:
(nbits > 1 ? val & mask : !!val) is a conditional operator:
if nbits >1 is true, then it's val&mask, aka the n lowest bits of val
if not, then it's !!val, aka "not not val" which evalauates to 0 if val is 0 and 1 if val is not 0.
In our case, it's the second alternative so 0 or 1 depending on val.
((nbits > 1 ? val & mask : !!val) << bitno) then shifts the 0 or the 1 by 2 bits.
现在终于将所有这些结合起来:
data = (data & ~(mask << bitno)) | ((nbits > 1 ? val & mask : !!val) << bitno);
= (data with the bit bitno set to 0) ored with (val expressed on one bit in the bit bitno, counting from the least significant )
另外说明,作为位值 0 与位值 x 的或运算结果为位值 x,此表达式将位 bitno 设置为 val(将 val 作为 bool 处理)。
但是工会应该做什么?
联合在同一内存位置处理其所有成员(它们都是同一类型 u8
)。
那么根据您的预期输出结果如下:
P.raw=0;
P.r2=1;
P.r3=0;
P.r4=1;
cout << (int)P.raw <<endl;
编写您的代码片段的乐观主义者当然期望结果为 20(又名二进制 10100)。这可能在许多编译器上都是这样工作的。但实际上,根据标准,这是绝对不能保证的:
9.5/1: In a union, at most one of the non-static data members can be active at any time, that is, the value of at most one of the non-static data members can be stored in a union at any time.
另外说明,如果您将某些内容存储在 r2 中,您不确定是否会在 r4 中找到相同的值。唯一可以肯定的是,如果你在 r2 中存储了一些东西并且没有在其他成员中存储任何其他东西,你将在 r2 中找到你存储在那里的东西。
工会的替代方案
如果需要保证便携性,可以考虑使用std::bitset
or standard bitfields。