如何设置、清除和切换一点?
当前回答
模板版本(放在头文件中),支持更改多位(适用于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输出。
其他回答
该程序基于@Jeremy的上述解决方案。如果有人想快速玩耍。
public class BitwiseOperations {
public static void main(String args[]) {
setABit(0, 4); // set the 4th bit, 0000 -> 1000 [8]
clearABit(16, 5); // clear the 5th bit, 10000 -> 00000 [0]
toggleABit(8, 4); // toggle the 4th bit, 1000 -> 0000 [0]
checkABit(8,4); // check the 4th bit 1000 -> true
}
public static void setABit(int input, int n) {
input = input | ( 1 << n-1);
System.out.println(input);
}
public static void clearABit(int input, int n) {
input = input & ~(1 << n-1);
System.out.println(input);
}
public static void toggleABit(int input, int n) {
input = input ^ (1 << n-1);
System.out.println(input);
}
public static void checkABit(int input, int n) {
boolean isSet = ((input >> n-1) & 1) == 1;
System.out.println(isSet);
}
}
Output :
8
0
0
true
更一般地,对于任意大小的位图:
#define BITS 8
#define BIT_SET( p, n) (p[(n)/BITS] |= (0x80>>((n)%BITS)))
#define BIT_CLEAR(p, n) (p[(n)/BITS] &= ~(0x80>>((n)%BITS)))
#define BIT_ISSET(p, n) (p[(n)/BITS] & (0x80>>((n)%BITS)))
将第n位设置为x(位值),不使用-1
有时,当您不确定-1或类似的结果时,您可能希望在不使用-1的情况下设置第n位:
number = (((number | (1 << n)) ^ (1 << n))) | (x << n);
解释:((number |(1<<n)将第n位设置为1(其中|表示逐位OR),然后使用(…)^(1<<n)将将第n个位设置为0,最后通过(…)|x<<n,将第n比特设置为0(位值)x。
这也适用于戈朗。
另一个选项是使用位字段:
struct bits {
unsigned int a:1;
unsigned int b:1;
unsigned int c:1;
};
struct bits mybits;
定义了一个3位字段(实际上是三个1位字段)。比特操作现在变得简单了一点(哈哈):
设置或清除一位:
mybits.b = 1;
mybits.c = 0;
要切换一位,请执行以下操作:
mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1; /* all work */
检查一点:
if (mybits.c) //if mybits.c is non zero the next line below will execute
这仅适用于固定大小的位字段。否则,您必须求助于前面文章中描述的比特旋转技术。
如何设置、清除和切换单个位?
要解决试图形成掩码时的常见编码陷阱,请执行以下操作: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;
与大多数位操作一样,最好使用无符号类型而不是有符号类型