如何设置、清除和切换一个位?
如何设置、清除和切换单个位?
为了解决在尝试形成掩码时常见的编码陷阱:
1
并不总是足够宽
当number
是比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
或者严谨地使用(uintmax_t)1
,让编译器进行优化。
number |= (1ull << x);
number |= ((uintmax_t)1 << x);
或者进行类型转换 - 这会导致在维护和检查代码时出现问题,需要确保类型转换正确并及时更新。
number |= (type_of_number)1 << x;
或者通过强制进行至少与number
类型一样宽的数学运算,轻松地将1
提升。
number |= (number*0 + 1) << x;
number |= (type_of_number)1 << x;
还是number |= (number*0 + 1) << x;
都不适用于设置有符号类型的符号位...事实上,number |= (1ull << x);
也不适用。有没有一种通过位置来实现可移植的方法呢? - chqrliepublic 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);
}
}
输出:
8
0
0
true
这是我使用的一些宏:
SET_FLAG(Status, Flag) ((Status) |= (Flag))
CLEAR_FLAG(Status, Flag) ((Status) &= ~(Flag))
INVALID_FLAGS(ulFlags, ulAllowed) ((ulFlags) & ~(ulAllowed))
TEST_FLAGS(t,ulMask, ulBit) (((t)&(ulMask)) == (ulBit))
IS_FLAG_SET(t,ulMask) TEST_FLAGS(t,ulMask,ulMask)
IS_FLAG_CLEAR(t,ulMask) TEST_FLAGS(t,ulMask,0)
一个模板化版本(放在头文件中),支持更改多个位(在AVR微控制器上工作):
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;
}
顺便提一下:如果不向编译器发送优化器参数(例如:-O3),则 constexpr 和 inline 不会使用。可以随意尝试代码,并在 https://godbolt.org/ 查看 ASM 输出。
(variable & bits == bits)
? - melpomene((variable & bits) == bits)
。 - Joakim L. Christiansenstd::bitset
。 - pqnet以下是 C 语言中执行基本位运算的例程:
#define INT_BIT (unsigned int) (sizeof(unsigned int) * 8U) //number of bits in unsigned int
int main(void)
{
unsigned int k = 5; //k is the bit position; here it is the 5th bit from the LSb (0th bit)
unsigned int regA = 0x00007C7C; //we perform bitwise operations on regA
regA |= (1U << k); //Set kth bit
regA &= ~(1U << k); //Clear kth bit
regA ^= (1U << k); //Toggle kth bit
regA = (regA << k) | regA >> (INT_BIT - k); //Rotate left by k bits
regA = (regA >> k) | regA << (INT_BIT - k); //Rotate right by k bits
return 0;
}
有时候当你不确定-1或类似的东西会导致什么结果时,你可能希望在不使用-1的情况下设置第n位:
number = (((number | (1 << n)) ^ (1 << n))) | (x << n);
解释: ((number | (1 << n)
将第n位设置为1(其中|
表示按位或),然后使用(...) ^ (1 << n)
将第n位设置为0,最后使用(...) | x << n)
将原来为0的第n位设置为(位值)x
。
这在golang
中也适用。
(number & ~(1 << n)) | (!!x << n)
。 - Will Eccles在C语言中,尝试使用以下函数来更改n位:
char bitfield;
// Start at 0th position
void chang_n_bit(int n, int value)
{
bitfield = (bitfield | (1 << n)) & (~( (1 << n) ^ (value << n) ));
}
或者
void chang_n_bit(int n, int value)
{
bitfield = (bitfield | (1 << n)) & ((value << n) | ((~0) ^ (1 << n)));
}
或者
void chang_n_bit(int n, int value)
{
if(value)
bitfield |= 1 << n;
else
bitfield &= ~0 ^ (1 << n);
}
char get_n_bit(int n)
{
return (bitfield & (1 << n)) ? 1 : 0;
}
value << n
可能导致未定义的行为。 - M.M1
更改为0x1
或1UL
以避免UB,@M.M正在谈论此事。 - user5550963