你如何设置,清除和切换一个位?

如何在C / C ++中设置,清除和切换?

设置一下

使用按位或运算符( | )设置一个位。

 number |= 1UL << x; 

这将设置位x

如果numberunsigned long更宽,则使用1ULL ; 1UL << x推进直到经过评估1UL << x其中UB的移动超过了一个long的宽度之后才会发生。 其余的例子也一样。

清理一下

使用按位AND运算符( & )清除一点。

 number &= ~(1UL << x); 

这将清除位x 。 您必须用位运算符( ~ )反转位串,然后进行与运算。

切换一下

XOR运算符( ^ )可以用来切换一点。

 number ^= 1UL << x; 

这将切换位x

检查一下

你没有要求这个,但我不妨补充一下。

要检查一下,把数字x移到右边,然后逐位AND:

 bit = (number >> x) & 1U; 

这将把位x的值放入可变bit

将第n位更改为x

将第n位设置为10可以通过2的补码C ++实现来实现:

 number ^= (-x ^ number) & (1UL << n); 

如果x1 ,则位n将被设置,并且如果x0 ,则位n被清除。 如果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); 

使用无符号types进行可移植位操作通常是一个好主意。

使用标准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版本允许运行时大小的位集。

另一种select是使用位域:

 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 

这只适用于固定大小的位域。 否则,你不得不求助于以前的文章中描述的那些颠覆性的技巧。

我使用头文件中定义的macros来处理位设置和清除:

 /* 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)) 

有时值得使用enum命名这些位:

 enum ThingFlags = { ThingMask = 0x0000, ThingFlag0 = 1 << 0, ThingFlag1 = 1 << 1, ThingError = 1 << 8, } 

然后再使用这个名字 。 即写

 thingstate |= ThingFlag1; thingstate &= ~ThingFlag0; if (thing & ThingError) {...} 

设置,清除和testing。 这样你就可以从代码的其余部分隐藏幻数。

除此之外,我赞同杰里米的解决scheme。

从snip-c.zip的bithop.how:

 /* ** 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))) 

好,让我们来分析一下

所有这些你似乎有问题的共同expression是“(1L <<(posn))”。 所有这一切是创build一个单一的位掩码,并将与任何整数types的工作。 “posn”参数指定你想要的位置。 如果posn == 0,那么这个expression式将评估为:

  0000 0000 0000 0000 0000 0000 0000 0001 binary. 

如果posn == 8,它将评估

  0000 0000 0000 0000 0000 0001 0000 0000 binary. 

换句话说,它只是在指定的位置创build一个1的字段。 唯一棘手的部分是BitClr()macros,我们需要在1的字段中设置一个0位。 这是通过使用由代字号(〜)运算符表示的相同expression式的1的补码来完成的。

一旦掩码被创build,就像你所build议的一样,通过使用按位和(&),或(|)和异或(^)操作符来将它应用于参数。 由于掩码是longtypes的,因此这些macros在char,short,int或long的情况下也能正常工作。

底线是这是一整套问题的一般解决scheme。 当然,每当你需要一个掩码的时候,重写这些macros的等价物是可能的,甚至是合适的,但是为什么呢? 请记住,macros的replace发生在预处理器中,所以生成的代码将反映这样一个事实,即编译器认为这些值是常量 – 也就是说,使用泛化macros以便每次需要“重新发明轮子”做点位操作。

不服气? 这里有一些testing代码 – 我使用了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] ——————————————- ———————-

对于初学者,我想用一个例子来解释一下:

例:

 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) 

| 操作员:设置位

 0101 0101 | 0000 1000 ___________ 0101 1101 (set the third bit without affecting other bits) 

使用按位运算符: & |

要在000b中设置最后一位:

 foo = foo | 001b 

要检查foo中的最后一位:

 if ( foo & 001b ) .... 

要清除foo中的最后一位:

 foo = foo & 110b 

为了清晰起见,我使用了XXXb。 您可能会使用hex表示,这取决于您打包位的数据结构。

这是我最喜欢的位算术macros,它适用于任何types的无符号整数数组,从unsigned charsize_t (这是应该有效处理的最大types):

 #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, ^=); 

testing一下:

 if (BITOP(array, bit, &)) ... 

等等

由于这是标记为“embedded”,我假设你正在使用一个微控制器。 以上所有build议都是有效的工作(读 – 修改 – 写,工会,结构等)。

然而,在一系列基于示波器的debugging过程中,我惊奇地发现,与直接向微处理器的PORTnSET / PORTnCLEAR寄存器写入值相比,这些方法在CPU周期中有相当大的开销,这对于紧密环路/高电平频率ISR的切换引脚。

对于那些不熟悉的:在我的例子中,微有一个普通的引脚状态寄存器PORTn,它反映了输出引脚,所以执行PORTn | = BIT_TO_SET会导致对该寄存器的读 – 修改 – 写操作。 然而,PORTnSET / PORTnCLEAR寄存器的值为1,表示“请将此位设置为1”(SET)或“请将此位设置为零”(CLEAR),将“0”表示“保留引脚”。 所以,最终会有两个端口地址,取决于您是设置还是清除位(不总是方便),而是反应速度更快,汇编代码更小。

位域方法在embedded式领域具有其他优点。 您可以定义一个直接映射到特定硬件寄存器中的位的结构。

 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第一,但这可能是实现相关的。 另外,请validation您的编译器处理程序字段如何跨越字节边界。

然后,您可以像以前一样读取,写入,testing各个值。

更一般的,对于任意大小的位图:

 #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))) 

在一个任意types的variables的任意位置检查一下:

 #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; } 

注:这是devise得很快(考虑到其灵活性)和非分支。 在编译Sun Studio 8时,会产生高效的SPARC机器代码; 我也在amd64上使用MSVC ++ 2008进行了testing。 可以使用类似的macros来设置和清除比特。 这个解决scheme与其他很多其他的关键区别在于它适用于任何types的variables。

如果你正在做很多事情,你可能想要使用口罩,这将使整个事情更快。 以下function非常快速而且仍然灵活(它们允许在任何大小的位图中旋转)。

 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等小端处理器,在内存中正确映射到对方(主要原因是小端处理器比大端处理器“更好”啊,我感觉到一场火焰战争来临。 ..)。

用这个:

 int ToggleNthBit ( unsigned char n, int num ) { if(num & (1 << n)) num &= ~(1 << n); else num |= (1 << n); return num; } 

该程序将任何数据位从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<<bitpos)); printf("%x\n", data); } } else { if (bit == 1) printf("elseif %x\n", data); else { data = (data|(bitvalue<<bitpos)); printf("else %x\n", data); } } } 

如果你想在linux内核中用c编程来执行这一切操作,那么我build议使用linux内核的标准apis。

请参阅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计算机上,这些操作也是保证primefaces性的 ,并且有助于保持处理器之间的一致性。

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个值。

我曾经用这种方法find重复的贷款logging,其中loan_number是ISAM密钥,使用6位数的贷款编号作为位数组的索引。 狂野地快速,经过8个月,certificate我们从中获取数据的大型机系统实际上是有故障的。 比特arrays的简单性使得他们对正确性的信心非常高 – 比如search方法。

Expanding on the bitset answer:

 #include <iostream> #include <bitset> #include <string> 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; } 

Use one of the operators as defined here .

To set a bit, used int x = x | 0x?; where ? is the bit position in binary form.

Here are some macros I use:

 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) 

How do you set, clear, and toggle a single bit?

To address a common coding pitfall when attempting to form the mask:
1 is not always wide enough

What problems happen when number is a wider type than 1 ?
x may be too great for the shift 1 << x leading to undefined behavior (UB). Even if x is not too great, ~ may not flip enough most-significant-bits.

 // 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 

To insure 1 is wide enough:

Code could use 1ull or pedantically (uintmax_t)1 and let the compiler optimize.

 number |= (1ull << x); number |= ((uintmax_t)1 << x); 

Or cast – which makes for coding/review/maintenance issues keeping the cast correct and up-to-date.

 number |= (type_of_number)1 << x; 

Or gently promote the 1 by forcing a math operation that is as least as wide as the type of number .

 number |= (number*0 + 1) << x; 

As with most bit manipulations, best to work with unsigned types rather than signed ones

For setting BitIdx -th bit in Number to BitValue

 Number = Number xor (1 shl BitIdx) or (BitValue shl BitIdx) 

The trick here is to first unconditionally clear the BitIdx -th bit by xor-ing it with 1. This version seems slightly slower than the one with branching ( if bit = 1 then setbit else clearbit ) but is a one-liner.

Try one of these functions in C language to change n bit

 char bitfield; // start at 0th position void chang_n_bit(int n, int value) { bitfield = (bitfield | (1 << n)) & (~( (1 << n) ^ (value << n) )); } 

要么

 void chang_n_bit(int n, int value) { bitfield = (bitfield | (1 << n)) & ((value << n) | ((~0) ^ (1 << n))); } 

要么

 void chang_n_bit(int n, int value) { if(value) bitfield |= 1 << n; else bitfield &= ~0 ^ (1 << n); } char get_n_bit(int n) { return (bitfield & (1 << n)) ? 1 : 0; }