如何在C/C++中设置,清除和切换?
使用按位OR运算符(|
)设置一个位.
number |= 1UL << n;
这将n
是最重要的number
.n
应该为零,如果要设置1
st位,依此类推n-1
,如果要设置该n
位.
使用1ULL
if number
比宽unsigned long
; 1UL << n
直到评估1UL << n
了未定义的行为偏移超过a的宽度之后才会发生促销long
.这同样适用于所有其他示例.
使用按位AND运算符(&
)清除一位.
number &= ~(1UL << n);
这将清除一n
点点number
.必须使用按位NOT运算符(~
)反转位串,然后运行AND.
XOR运算符(^
)可用于切换位.
number ^= 1UL << n;
这将切换到n
第二位number
.
你没有要求这个,但我不妨补充一下.
要检查一下,将数字n向右移动,然后按位向右移动:
bit = (number >> n) & 1U;
这将把n
第四位的值number
放入变量中bit
.
将该n
位设置为1
或者0
可以通过以下2的补码C++实现来实现:
number ^= (-x ^ number) & (1UL << n);
位n
,如果将被设置x
为1
,如果清除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);
使用无符号类型进行便携式位操作通常是个好主意.
要么
number = (number & ~(1UL << n)) | (x << n);
(number & ~(1UL << n))
将清除该n
位并将(x << n)
该n
位置位x
.
一般来说,通常不要复制/粘贴代码也是一个好主意,因此许多人使用预处理器宏(如社区维基回答更进一步)或某种封装.
使用标准C++库:std::bitset
.
或Boost版本:boost::dynamic_bitset
.
没有必要自己动手:
#include#include 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
与标准库编译时大小的bitset相比,Boost版本允许运行时大小的bitset.
另一种选择是使用位字段:
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
这仅适用于固定大小的位字段.否则你必须采用之前帖子中描述的比特技巧.
我使用头文件中定义的宏来处理位集和清除:
/* 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)))) // '!!' to make sure this returns 0 or 1 /* 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))
它有时是值得使用enum
来命名的位:
enum ThingFlags = { ThingMask = 0x0000, ThingFlag0 = 1 << 0, ThingFlag1 = 1 << 1, ThingError = 1 << 8, }
然后使用名称.即写
thingstate |= ThingFlag1; thingstate &= ~ThingFlag0; if (thing & ThingError) {...}
设置,清除和测试.这样您就可以隐藏其余代码中的幻数.
除此之外,我赞同杰里米的解决方案.
/* ** 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或long上也能正常工作.
最重要的是,这是一整类问题的一般解决方案.当然,每次需要时,使用显式掩码值重写这些宏的等效值是可能的,甚至是适当的,但为什么呢?请记住,宏替换发生在预处理器中,因此生成的代码将反映编译器认为值是常量的事实 - 即,每次需要时使用通用宏来"重新发明轮子"同样有效做点操作.
不服气?这里有一些测试代码 - 我使用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(disassembled)] -------------------------------------- ---------
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] ------------------------------------------- ----------------------
使用按位运算符: &
|
要设置最后一位000b
:
foo = foo | 001b
检查最后一位foo
:
if ( foo & 001b ) ....
清除最后一点foo
:
foo = foo & 110b
我用来XXXb
表示清晰.您可能正在使用HEX表示,具体取决于您打包位的数据结构.
对于初学者,我想用一个例子来解释一下:
例:
value is 0x55; bitnum : 3rd.
该&
运算符用于检查一下:
0101 0101 & 0000 1000 ___________ 0000 0000 (mean 0: False). It will work fine if the third bit is 1 (then the answer will be True)
切换或翻转:
0101 0101 ^ 0000 1000 ___________ 0101 1101 (Flip the third bit without affecting other bits)
|
operator:设置位
0101 0101 | 0000 1000 ___________ 0101 1101 (set the third bit without affecting other bits)
这里是我最喜欢的位算术宏,这对于任何类型的无符号整数数组的作品从unsigned char
达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, &)) ...
等等
由于这被标记为"嵌入式",我假设您正在使用微控制器.所有上述建议都是有效的和工作(读 - 修改 - 写,工会,结构等).
但是,在基于示波器的调试过程中,我惊讶地发现,与直接将值写入微型PORTnSET/PORTnCLEAR寄存器相比,这些方法在CPU周期中具有相当大的开销,这使得存在紧密环路/高电平时会产生真正的差异-frequency ISR的切换引脚.
对于那些不熟悉的人:在我的例子中,micro有一个通用的引脚状态寄存器PORTn,它反映了输出引脚,所以做PORTn | = BIT_TO_SET会导致对该寄存器的读 - 修改 - 写.但是,PORTnSET/PORTnCLEAR寄存器取"1"表示"请将此位1"(SET)或"请将此位置零"(CLEAR),将"0"表示"保持引脚单独".所以,你最终有两个端口地址取决于不管你是设置或清除位(并不总是很方便),但很多更快的反应和更小的汇编代码.
位域方法在嵌入式领域具有其他优势.您可以定义一个直接映射到特定硬件寄存器中的位的结构.
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测试了它.可以制作类似的宏来设置和清除位.与其他许多解决方案相比,此解决方案的主要区别在于它适用于几乎任何类型的变量中的任何位置.
更一般地说,对于任意大小的位图:
#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)))
如果你做了很多事情,你可能想要使用面具,这将使整个事情变得更快.以下函数非常快并且仍然灵活(它们允许在任何大小的位图中进行位错).
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注意,要在16位整数中设置位'n',请执行以下操作:
TSetBit( n, &my_int);由您来确保位数在您传递的位图范围内.请注意,对于小字节,处理器,字节,字,双字,qword等,在内存中正确映射(小端处理器比大端处理器"更好"的主要原因啊,我觉得火焰战即将到来上...).
不要将表用于可以使用单个运算符实现的函数.TQuickByteMask [n]相当于(1 << n).此外,让你的论点简短是一个非常糟糕的主意./和%实际上是一个除法,而不是bitshift/bitwise,因为2的幂的有符号除法不能按位实现.你应该让参数类型为unsigned int!
对于大/小端,big endian将以相同的方式映射整数和原始数据(例如字符串):从整个位图到左到右msb到lsb.虽然小端将从左到右的整数映射为7-0,15-8,23-18,31-24,但原始数据仍然是从左到右的msb到lsb.因此,对于您的特定算法来说,小端更好的方式完全超出我的范围,它似乎恰恰相反.
@R ..如果你的平台无法有效地转移,如旧的微芯片设备,表格可能很有用,但当然样本中的划分绝对是低效的
15> 小智..:该程序将任何数据位从0更改为1或1更改为0:
{ unsigned int data = 0x000000F0; int bitpos = 4; int bitvalue = 1; unsigned int bit = data; bit = (bit>>bitpos)&0x00000001; int invbitvalue = 0x00000001&(~bitvalue); printf("%x\n",bit); if (bitvalue == 0) { if (bit == 0) printf("%x\n", data); else { data = (data^(invbitvalue<
16> 小智..:用这个:
int ToggleNthBit ( unsigned char n, int num ) { if(num & (1 << n)) num &= ~(1 << n); else num |= (1 << n); return num; }
好吧,它使用低效的分支.
@asdf编译器的工作是输出最有效的二进制文件,程序员的工作就是编写清晰的代码
这是测试,设置和清除特定位的良好演示.然而,这是一个非常糟糕的方法来切换一点.
17> kendotwill..:扩大
bitset
答案:#include#include #include using namespace std; int main() { bitset<8> byte(std::string("10010011"); // Set Bit byte.set(3); // 10010111 // Clear Bit byte.reset(2); // 10010101 // Toggle Bit byte.flip(7); // 00010101 cout << byte << endl; return 0; }
18> Jeegar Patel..:如果你想在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计算机上都是原子的,并且有助于保持处理器之间的一致性.
19> 小智..:Visual C 2010,也许还有许多其他编译器,都直接支持内置的位操作.令人惊讶的是,即使sizeof()运算符也能正常工作.
bool IsGph[256], IsNotGph[256]; // Initialize boolean array to detect printable characters for(i=0; i所以,对于你的问题,IsGph [i] = 1,或IsGph [i] = 0使设置和清除bool变得容易.
要找到不可打印的字符......
// 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请注意,此代码没有任何"特殊".它有点像整数 - 在技术上,它是.1位整数,可以容纳2个值,仅包含2个值.
我曾经使用这种方法找到重复的贷款记录,其中loan_number是ISAM密钥,使用6位数的贷款号作为位数组的索引.野蛮的快速,并在8个月后,证明我们从中获取数据的主机系统实际上是故障.比特阵列的简单性使得它们的正确性非常高 - 例如,与搜索方法相比.
这为每个`bool`至少使用了整个字节的存储空间.对于使用`int`来实现`bool`的C89设置,甚至可能是4个字节
@RocketRoy:可能值得改变声称这是"位操作"的例子的句子.
20> 小智..:使用此处定义的运算符之一.
要设置一个位,用来
int x = x | 0x?;
在那里?
是二进制形式的比特位置.
`0x`是十六进制的文字的前缀,而不是二进制.
21> Sazzad Hissa..:int set_nth_bit(int num, int n){ return (num | 1 << n); } int clear_nth_bit(int num, int n){ return (num & ~( 1 << n)); } int toggle_nth_bit(int num, int n){ return num ^ (1 << n); } int check_nth_bit(int num, int n){ return num & (1 << n); }
22> 小智..:使用变量
int value, pos;值-数据
位-我们想要设置,清除或切换的位的位置。设置一点:
value = value | 1 << pos;清除一点:
value = value & ~(1 << pos);切换一下:
value = value ^ 1 << pos;
23> 小智..:这是我使用的一些宏:
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)