如何设置、清除和切换一点?
当前回答
如何设置、清除和切换单个位?
要解决试图形成掩码时的常见编码陷阱,请执行以下操作: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;
与大多数位操作一样,最好使用无符号类型而不是有符号类型
其他回答
另一个选项是使用位字段:
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
这仅适用于固定大小的位字段。否则,您必须求助于前面文章中描述的比特旋转技术。
比特场方法在嵌入式领域还有其他优势。您可以定义直接映射到特定硬件寄存器中的位的结构。
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,但这可能取决于实现。此外,验证编译器处理程序字段如何跨越字节边界。
然后,您可以像以前一样读取、写入和测试各个值。
先假设几件事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)
推荐阅读-按位操作员练习
更一般地,对于任意大小的位图:
#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)))
以下是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;
}