如何设置,清除和切换单个位?
如何在C / C ++中设置,清除和切换位?
解决方案
回答
使用按位运算符:&``|
要设置" 000b"的最后一位:
foo = foo | 001b
要检查foo
中的最后一位:
if ( foo & 001b ) ....
要清除foo
中的最后一位:
foo = foo & 110b
为了清楚起见,我使用了" XXXb"。我们可能将要使用HEX表示形式,具体取决于要打包位的数据结构。
回答
使用按位或者运算符(|
)进行设置。
number |= 1UL << n;
那将设置number的第n位。
如果number
的宽度大于unsigned long
的宽度,则使用1ULL
;直到评估" 1UL << n"之后,才发生" 1UL << n"的提升,在这种情况下,未定义的行为是移动超过" long"的宽度。其他所有示例也是如此。
使用按位AND运算符(&
)清除一点。
number &= ~(1UL << n);
这将清除number的第n位。我们必须使用按位NOT运算符(〜
)反转位字符串,然后将其取反。
XOR运算符(^
)可用于切换一位。
number ^= 1UL << n;
这将切换数字的第n位。
我们没有要求这样做,但我也可以添加它。
要检查一点,将数字n右移,然后按位与它相加:
bit = (number >> n) & 1U;
这会将number的第n位的值放入变量bit中。
在2的补码C ++实现中,可以将第n位设置为1或者0来实现:
number ^= (-x ^ number) & (1UL << n);
如果x为1则将设置n位,如果x为0则将其清除。如果x具有其他值,则会产生垃圾。 x = !! x会将其布尔值设为0或者1.
为了使它独立于2的补码取反行为(其中-1设置了所有位,与1的补码或者符号/幅度C ++实现不同),请使用无符号取反。
number ^= (-(unsigned long)x ^ number) & (1UL << n);
或者
unsigned long newbit = !!x; // Also booleanize to force 0 or 1 number ^= (-newbit ^ number) & (1UL << n);
使用无符号类型进行可移植位操作通常是一个好主意。
通常也不要复制/粘贴代码,这也是一个好主意,因此许多人使用预处理器宏(例如社区Wiki进一步解答)或者某种封装。
回答
有时值得用一个"枚举"来命名这些位:
enum ThingFlags = { ThingMask = 0x0000, ThingFlag0 = 1 << 0, ThingFlag1 = 1 << 1, ThingError = 1 << 8, }
然后使用这些名称。 IE。写
thingstate |= ThingFlag1; thingstate &= ~ThingFlag0; if (thing & ThingError) {...}
进行设置,清除和测试。这样,我们可以从其余代码中隐藏幻数。
除此之外,我赞同杰里米的解决方案。
回答
另一种选择是使用位字段:
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
这仅适用于固定大小的位字段。否则,我们必须诉诸先前文章中介绍的位旋转技术。
回答
从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.
换句话说,它只是在指定的位置创建一个0字段,其中一个为1
位置。唯一棘手的部分是我们需要设置的BitClr()宏中
1字段中的单个0位。这是通过使用1来完成的
由波浪号(〜)运算符表示的相同表达式的补码。
一旦创建了遮罩,便会按照建议将其应用于自变量,
通过使用按位和(&)或者(|)和xor(^)运算符。自面膜
的类型为long,则宏在char,short,int上也能正常工作,
或者长的。
最重要的是,这是对整个类别的通用解决方案
问题。当然,有可能甚至适当地重写
每次我们都可以使用显式掩码值等效于任何这些宏
需要一个,但是为什么呢?请记住,宏替换发生在
预处理程序,因此生成的代码将反映以下事实:
被编译器视为常量,即使用效率相同
通用宏,可以在我们每次需要做的时候"重新发明轮子"
位操作。
不服气?这是我使用Watcom C进行全面优化的一些测试代码
而不使用_cdecl,因此产生的反汇编将与
可能的:
---- [TEST.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; }
---- [TEST.OUT(已分解)] -------------------------------------- ---------
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
---- [finis] ------------------------------------------- ----------------------
回答
如果我们要花很多时间在摆弄,我们可能要使用遮罩,这样可以使整个过程更快。以下功能非常快且仍然很灵活(它们允许在任何大小的位图中旋转位)。
const unsigned char TQuickByteMask[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, }; /** Set bit in any sized bit mask. * * @return none * * @param bit - Bit number. * @param bitmap - Pointer to bitmap. */ void TSetBit( short bit, unsigned char *bitmap) { short n, x; x = bit / 8; // Index to byte. n = bit % 8; // Specific bit in byte. bitmap[x] |= TQuickByteMask[n]; // Set bit. } /** Reset bit in any sized mask. * * @return None * * @param bit - Bit number. * @param bitmap - Pointer to bitmap. */ void TResetBit( short bit, unsigned char *bitmap) { short n, x; x = bit / 8; // Index to byte. n = bit % 8; // Specific bit in byte. bitmap[x] &= (~TQuickByteMask[n]); // Reset bit. } /** Toggle bit in any sized bit mask. * * @return none * * @param bit - Bit number. * @param bitmap - Pointer to bitmap. */ void TToggleBit( short bit, unsigned char *bitmap) { short n, x; x = bit / 8; // Index to byte. n = bit % 8; // Specific bit in byte. bitmap[x] ^= TQuickByteMask[n]; // Toggle bit. } /** Checks specified bit. * * @return 1 if bit set else 0. * * @param bit - Bit number. * @param bitmap - Pointer to bitmap. */ short TIsBitSet( short bit, const unsigned char *bitmap) { short n, x; x = bit / 8; // Index to byte. n = bit % 8; // Specific bit in byte. // Test bit (logigal AND). if (bitmap[x] & TQuickByteMask[n]) return 1; return 0; } /** Checks specified bit. * * @return 1 if bit reset else 0. * * @param bit - Bit number. * @param bitmap - Pointer to bitmap. */ short TIsBitReset( short bit, const unsigned char *bitmap) { return TIsBitSet(bit, bitmap) ^ 1; } /** Count number of bits set in a bitmap. * * @return Number of bits set. * * @param bitmap - Pointer to bitmap. * @param size - Bitmap size (in bits). * * @note Not very efficient in terms of execution speed. If you are doing * some computationally intense stuff you may need a more complex * implementation which would be faster (especially for big bitmaps). * See (http://graphics.stanford.edu/~seander/bithacks.html). */ int TCountBits( const unsigned char *bitmap, int size) { int i, count = 0; for (i=0; i<size; i++) if (TIsBitSet(i, bitmap)) count++; return count; }
请注意,要以16位整数设置位" n",请执行以下操作:
TSetBit( n, &my_int);
我们需要确保位数在我们传递的位图范围内。请注意,对于字节,字,双字,qword等在内存中相互正确映射的小字节序处理器(主要原因是,小字节序处理器比大字节序处理器"更好",是啊,我感到火焰大战即将来临在...)。
回答
使用标准C ++库:std :: bitset <N>。
或者Boost版本:boost :: dynamic_bitset
。
无需自己动手:
#include <bitset> #include <iostream> int main() { std::bitset<5> x; x[1] = 1; x[2] = 0; // Note x[0-4] valid std::cout << x << std::endl; }
[Alpha:] > ./a.out 00010
与标准库编译时大小的位组相比,Boost版本允许运行时大小的位组。
回答
我使用在头文件中定义的宏来处理位设置和清除:
/* a=target variable, b=bit number to act upon 0-n */ #define BIT_SET(a,b) ((a) |= (1ULL<<(b))) #define BIT_CLEAR(a,b) ((a) &= ~(1ULL<<(b))) #define BIT_FLIP(a,b) ((a) ^= (1ULL<<(b))) #define BIT_CHECK(a,b) ((a) & (1ULL<<(b))) /* x=target variable, y=mask */ #define BITMASK_SET(x,y) ((x) |= (y)) #define BITMASK_CLEAR(x,y) ((x) &= (~(y))) #define BITMASK_FLIP(x,y) ((x) ^= (y)) #define BITMASK_CHECK_ALL(x,y) (((x) & (y)) == (y)) // warning: evaluates y twice #define BITMASK_CHECK_ANY(x,y) ((x) & (y))
回答
位域方法在嵌入式领域具有其他优势。我们可以定义直接映射到特定硬件寄存器中位的结构。
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,但这可能与实现有关。另外,请验证编译器如何处理跨越字节边界的字段。
然后,我们可以像以前一样读取,写入,测试各个值。
回答
在任意类型的变量的任意位置检查一点:
#define bit_test(x, y) ( ( ((const char*)&(x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )
用法示例:
int main(void) { unsigned char arr[8] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }; for (int ix = 0; ix < 64; ++ix) printf("bit %d is %d\n", ix, bit_test(arr, ix)); return 0; }
笔记:
这被设计为快速(鉴于其灵活性)和非分支。编译Sun Studio 8时,它可以产生有效的SPARC机器代码。我还在amd64上使用MSVC ++ 2008测试了它。可以制作相似的宏来设置和清除位。与许多其他解决方案相比,此解决方案的主要区别在于,它几乎可以在任何类型的变量中的任何位置使用。
回答
用这个:
int ToggleNthBit ( unsigned char n, int num ) { if(num & (1 << n)) num &= ~(1 << n); else num |= (1 << n); return num; }
回答
更一般的,对于任意大小的位图:
#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)))
回答
这是我最喜欢的位算术宏,它适用于从unsigned char到size_t的任何类型的unsigned integer数组(这是应该有效使用的最大类型):
#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, &)) ...
等等。