如何设置、清除和切换一点?
当前回答
先假设几件事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)
推荐阅读-按位操作员练习
其他回答
由于这被标记为“嵌入式”,我假设您使用的是微控制器。以上所有建议都是有效的&有效的(读、修改、写、联合、结构等)。
然而,在一场基于示波器的调试中,我惊奇地发现,与直接将值写入微控制器的PORTnSET/PORTnCLEAR寄存器相比,这些方法在CPU周期中具有相当大的开销,这在存在紧密环路/高频ISR的切换引脚的情况下产生了真正的差异。
对于那些不熟悉的人:在我的示例中,micro有一个反映输出引脚的通用引脚状态寄存器PORTn,因此执行PORTn |=BIT_TO_SET会导致对该寄存器的读-修改-写入。然而,PORTnSET/PORTnCLEAR寄存器取“1”表示“请将此位置为1”(SET)或“请将该位置为零”(CLEAR),取“0”表示“不使用管脚”。因此,您最终会得到两个端口地址,这取决于您是设置还是清除位(并不总是方便),但反应更快,汇编代码更小。
另一个选项是使用位字段:
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
这仅适用于固定大小的位字段。否则,您必须求助于前面文章中描述的比特旋转技术。
Visual C 2010,也许还有许多其他编译器,都直接支持内置的布尔运算。一个位有两个可能的值,就像一个布尔值一样,所以我们可以使用布尔值,即使在这种表示中,布尔值占用的内存空间比一个位多。这是有效的,即使sizeof()运算符也能正常工作。
bool IsGph[256], IsNotGph[256];
// Initialize boolean array to detect printable characters
for(i=0; i<sizeof(IsGph); i++) {
IsGph[i] = isgraph((unsigned char)i);
}
因此,对于您的问题,IsGph[i]=1或IsGph[i]=0使得设置和清除布尔值变得容易。
要查找不可打印的字符:
// Initialize boolean array to detect UN-printable characters,
// then call function to toggle required bits true, while initializing a 2nd
// boolean array as the complement of the 1st.
for(i=0; i<sizeof(IsGph); i++) {
if(IsGph[i]) {
IsNotGph[i] = 0;
} else {
IsNotGph[i] = 1;
}
}
注意,这段代码没有什么“特殊”之处。它有点像一个整数-从技术上讲,它是一个1位整数,可以容纳2个值,并且只能容纳2个。
我曾经使用这种方法查找重复的贷款记录,其中loan_number是ISAM密钥,使用6位贷款编号作为位数组的索引。8个月后,我们以惊人的速度证明了我们从中获取数据的主机系统实际上发生了故障。比特阵列的简单性使人们对其正确性的信心非常高——例如,与搜索方法相比。
先假设几件事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)
推荐阅读-按位操作员练习
从snip-c.zip的bitops.h:
/*
** Bit set, clear, and test operations
**
** public domain snippet by Bob Stout
*/
typedef enum {ERROR = -1, FALSE, TRUE} LOGICAL;
#define BOOL(x) (!(!(x)))
#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))
好的,让我们分析一下。。。
在所有这些问题中,您似乎遇到的常见表达是“(1L<<(posn))”。所有这些都是创建一个带有单个位的掩码并且可以与任何整数类型一起工作。“posn”参数指定把钻头放在你想要的位置。如果posn==0,则此表达式将评估为:
0000 0000 0000 0000 0000 0000 0000 0001 binary.
如果posn==8,它将评估为:
0000 0000 0000 0000 0000 0001 0000 0000 binary.
换句话说,它只需在指定的位置唯一棘手的部分是我们需要设置的BitClr()宏1字段中的单个0位。这是通过使用1与颚化符(~)运算符表示的表达式相同的补码。
一旦创建了掩码,它将按照您的建议应用于参数,使用按位和(&)、或(|)和xor(^)运算符。自从面具类型为long,宏将在char、short、int、,或长的。
底线是,这是一个通用的解决方案问题。当然,重写相当于每次使用显式掩码值的任何宏需要一个,但为什么要这样做?请记住,宏替换发生在因此生成的代码将反映以下事实:被编译器认为是常量,即使用起来同样有效每当你需要做的时候,通用宏都会“重新发明轮子”比特操作。
不服气?下面是一些测试代码-我使用了Watcom C,并进行了全面优化并且不使用_cdecl,因此生成的反汇编将与可能:
----[测试.C]----------------------------------------------------------------
#define BOOL(x) (!(!(x)))
#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))
int bitmanip(int word)
{
word = BitSet(word, 2);
word = BitSet(word, 7);
word = BitClr(word, 3);
word = BitFlp(word, 9);
return word;
}
----[测试输出(拆卸)]-----------------------------------------------
Module: C:\BINK\tst.c
Group: 'DGROUP' CONST,CONST2,_DATA,_BSS
Segment: _TEXT BYTE 00000008 bytes
0000 0c 84 bitmanip_ or al,84H ; set bits 2 and 7
0002 80 f4 02 xor ah,02H ; flip bit 9 of EAX (bit 1 of AH)
0005 24 f7 and al,0f7H
0007 c3 ret
No disassembly errors
----[完成]-----------------------------------------------------------------