如何设置、清除和切换一点?
当前回答
这是我最喜欢的位算术宏,它适用于从无符号字符到size_t的任何类型的无符号整数数组(这是应该有效使用的最大类型):
#define BITOP(a,b,op) \
((a)[(size_t)(b)/(8*sizeof *(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof *(a)))))
要设置位:
BITOP(array, bit, |=);
要清除一点:
BITOP(array, bit, &=~);
要切换一位,请执行以下操作:
BITOP(array, bit, ^=);
要测试一点:
if (BITOP(array, bit, &)) ...
etc.
其他回答
设置一位
使用按位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答案)或某种封装。
有时值得使用枚举来命名位:
enum ThingFlags = {
ThingMask = 0x0000,
ThingFlag0 = 1 << 0,
ThingFlag1 = 1 << 1,
ThingError = 1 << 8,
}
然后稍后使用这些名称,即写
thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}
设置、清除和测试。通过这种方式,您可以对代码的其余部分隐藏神奇的数字。
除此之外,我支持佩奇·鲁滕的解决方案。
以下是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;
}
如果您想在Linux内核中使用C编程执行所有操作,那么我建议使用Linux内核的标准API。
看见https://www.kernel.org/doc/htmldocs/kernel-api/ch02s03.html
set_bit Atomically set a bit in memory
clear_bit Clears a bit in memory
change_bit Toggle a bit in memory
test_and_set_bit Set a bit and return its old value
test_and_clear_bit Clear a bit and return its old value
test_and_change_bit Change a bit and return its old value
test_bit Determine whether a bit is set
注意:这里整个操作在一个步骤中完成。因此,即使在SMP计算机上,这些都保证是原子的,并且是有用的以保持处理器之间的一致性。
比特场方法在嵌入式领域还有其他优势。您可以定义直接映射到特定硬件寄存器中的位的结构。
struct HwRegister {
unsigned int errorFlag:1; // one-bit flag field
unsigned int Mode:3; // three-bit mode field
unsigned int StatusCode:4; // four-bit status code
};
struct HwRegister CR3342_AReg;
您需要注意位打包顺序-我认为它首先是MSB,但这可能取决于实现。此外,验证编译器处理程序字段如何跨越字节边界。
然后,您可以像以前一样读取、写入和测试各个值。