如何设置、清除和切换一点?
当前回答
展开位集答案:
#include <iostream>
#include <bitset>
#include <string>
using namespace std;
int main() {
bitset<8> byte(std::string("10010011");
// Set Bit
byte.set(3); // 10010111
// Clear Bit
byte.reset(2); // 10010101
// Toggle Bit
byte.flip(7); // 00010101
cout << byte << endl;
return 0;
}
其他回答
模板版本(放在头文件中),支持更改多位(适用于AVR微控制器btw):
namespace bit {
template <typename T1, typename T2>
constexpr inline T1 bitmask(T2 bit)
{return (T1)1 << bit;}
template <typename T1, typename T3, typename ...T2>
constexpr inline T1 bitmask(T3 bit, T2 ...bits)
{return ((T1)1 << bit) | bitmask<T1>(bits...);}
/** Set these bits (others retain their state) */
template <typename T1, typename ...T2>
constexpr inline void set (T1 &variable, T2 ...bits)
{variable |= bitmask<T1>(bits...);}
/** Set only these bits (others will be cleared) */
template <typename T1, typename ...T2>
constexpr inline void setOnly (T1 &variable, T2 ...bits)
{variable = bitmask<T1>(bits...);}
/** Clear these bits (others retain their state) */
template <typename T1, typename ...T2>
constexpr inline void clear (T1 &variable, T2 ...bits)
{variable &= ~bitmask<T1>(bits...);}
/** Flip these bits (others retain their state) */
template <typename T1, typename ...T2>
constexpr inline void flip (T1 &variable, T2 ...bits)
{variable ^= bitmask<T1>(bits...);}
/** Check if any of these bits are set */
template <typename T1, typename ...T2>
constexpr inline bool isAnySet(const T1 &variable, T2 ...bits)
{return variable & bitmask<T1>(bits...);}
/** Check if all these bits are set */
template <typename T1, typename ...T2>
constexpr inline bool isSet (const T1 &variable, T2 ...bits)
{return ((variable & bitmask<T1>(bits...)) == bitmask<T1>(bits...));}
/** Check if all these bits are not set */
template <typename T1, typename ...T2>
constexpr inline bool isNotSet (const T1 &variable, T2 ...bits)
{return ((variable & bitmask<T1>(bits...)) != bitmask<T1>(bits...));}
}
使用示例:
#include <iostream>
#include <bitset> // for console output of binary values
// and include the code above of course
using namespace std;
int main() {
uint8_t v = 0b1111'1100;
bit::set(v, 0);
cout << bitset<8>(v) << endl;
bit::clear(v, 0,1);
cout << bitset<8>(v) << endl;
bit::flip(v, 0,1);
cout << bitset<8>(v) << endl;
bit::clear(v, 0,1,2,3,4,5,6,7);
cout << bitset<8>(v) << endl;
bit::flip(v, 0,7);
cout << bitset<8>(v) << endl;
}
BTW:如果不向编译器发送优化器参数(例如:-O3),则不使用constexpr和inline。请随时尝试以下代码https://godbolt.org/并查看ASM输出。
设置一位
使用按位OR运算符(|)设置位。
number |= 1UL << n;
这将设置数字的第n位。如果要将第1位设置为n-1,那么n应该为零,如果要设置第n位。
如果数字大于无符号长,则使用1ULL;1UL<<n的提升直到评估1UL<<n之后才发生,其中移动超过长的宽度是未定义的行为。这同样适用于所有其他示例。
清除一点
使用位AND运算符(&)清除位。
number &= ~(1UL << n);
这将清除数字的第n位。必须使用按位NOT运算符(~)反转位字符串,然后对其进行AND运算。
稍微扭动一下
XOR运算符(^)可用于切换位。
number ^= 1UL << n;
这将切换数字的第n位。
检查一点
你没有要求这样做,但我不妨补充一下。
要检查一位,请向右移动数字n,然后按位“与”:
bit = (number >> n) & 1U;
这将把数字第n位的值放入变量位。
将第n位更改为x
在2的补码C++实现中,可以通过以下方式将第n位设置为1或0:
number ^= (-x ^ number) & (1UL << n);
如果x为1,则设置位n,如果x为0,则清除位n。如果x有其他值,则会得到垃圾。x=!!x将其布尔化为0或1。
要使其独立于2的补码否定行为(其中-1设置了所有位,与1的补码或符号/大小C++实现不同),请使用无符号否定。
number ^= (-(unsigned long)x ^ number) & (1UL << n);
or
unsigned long newbit = !!x; // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);
使用无符号类型进行可移植位操作通常是一个好主意。
or
number = (number & ~(1UL << n)) | (x << n);
(number&~(1UL<<n))将清除第n位,(x<<n)将第n位设置为x。
一般来说,不复制/粘贴代码也是一个好主意,因为很多人使用预处理器宏(如社区wiki答案)或某种封装。
先假设几件事num=55整数以执行逐位操作(set、get、clear、toggle)。n=4 0位位置,以执行逐位操作。
如何获得一点?
要获得num的第n位,请右移num,n次。然后用1执行逐位AND&。
bit = (num >> n) & 1;
它是如何工作的?
0011 0111 (55 in decimal)
>> 4 (right shift 4 times)
-----------------
0000 0011
& 0000 0001 (1 in decimal)
-----------------
=> 0000 0001 (final result)
如何设置一点?
设置数字的特定位。左移1 n次。然后用num。
num |= (1 << n); // Equivalent to; num = (1 << n) | num;
它是如何工作的?
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
0001 0000
| 0011 0111 (55 in decimal)
-----------------
=> 0001 0000 (final result)
如何清除一点?
左移1,n次,即1<<n。对上述结果执行按位补码。因此,第n位变为未置位,其余位变为置位,即~(1<<n)。最后,对上述结果和num执行逐位AND&运算。上述三个步骤一起可以写成num&(~(1<<n));
num &= (~(1 << n)); // Equivalent to; num = num & (~(1 << n));
它是如何工作的?
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
~ 0001 0000
-----------------
1110 1111
& 0011 0111 (55 in decimal)
-----------------
=> 0010 0111 (final result)
如何切换一点?
要切换位,我们使用逐位XOR^运算符。如果两个操作数的对应位不同,则逐位XOR运算符的计算结果为1,否则计算结果为0。
这意味着要切换一个位,我们需要对要切换的位和1执行XOR运算。
num ^= (1 << n); // Equivalent to; num = num ^ (1 << n);
它是如何工作的?
如果要切换的位为0,则0 ^1=>1。如果要切换的位为1,则1^1=>0。
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
0001 0000
^ 0011 0111 (55 in decimal)
-----------------
=> 0010 0111 (final result)
推荐阅读-按位操作员练习
对于初学者,我想用一个例子解释一下:
例子:
value is 0x55;
bitnum : 3rd.
使用&运算符检查位:
0101 0101
&
0000 1000
___________
0000 0000 (mean 0: False). It will work fine if the third bit is 1 (then the answer will be True)
切换或翻转:
0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)
|运算符:设置位
0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)
如何设置、清除和切换单个位?
要解决试图形成掩码时的常见编码陷阱,请执行以下操作:1并不总是足够宽
当数字的类型大于1时会出现什么问题?x对于移位1<<x可能太大,导致未定义的行为(UB)。即使x不太大,~也可能无法翻转足够多的最高有效位。
// assume 32 bit int/unsigned
unsigned long long number = foo();
unsigned x = 40;
number |= (1 << x); // UB
number ^= (1 << x); // UB
number &= ~(1 << x); // UB
x = 10;
number &= ~(1 << x); // Wrong mask, not wide enough
要确保1足够宽:
代码可以使用1ull或pedatically(uintmax_t)1并让编译器进行优化。
number |= (1ull << x);
number |= ((uintmax_t)1 << x);
或铸造-这使得编码/审查/维护问题保持铸造正确和最新。
number |= (type_of_number)1 << x;
或者通过强制一个至少与数字类型一样宽的数学运算来温和地推广1。
number |= (number*0 + 1) << x;
与大多数位操作一样,最好使用无符号类型而不是有符号类型