C++ 您如何设置、清除和切换单个位?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/47981/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 12:27:28  来源:igfitidea点击:

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

c++cbit-manipulationbitwise-operators

提问by JeffV

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

你如何设置、清除和切换一点?

回答by Jeremy Ruten

Setting a bit

设置一点

Use the bitwise OR operator (|) to set a bit.

使用按位 OR 运算符 ( |) 设置位。

number |= 1UL << n;

That will set the nth bit of number. nshould be zero, if you want to set the 1st bit and so on upto n-1, if you want to set the nth bit.

这将设置 的n第 位numbern应该为零,如果你想设置1st 位等等n-1,如果你想设置nth 位。

Use 1ULLif numberis wider than unsigned long; promotion of 1UL << ndoesn't happen until after evaluating 1UL << nwhere it's undefined behaviour to shift by more than the width of a long. The same applies to all the rest of the examples.

使用1ULLifnumber大于unsigned long; 推广的1UL << n情况不会发生,直到评估后1UL << n它的比的宽度更不确定的行为转变long。这同样适用于所有其余示例。

Clearing a bit

清零一点

Use the bitwise AND operator (&) to clear a bit.

使用按位与运算符 ( &) 清除位。

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

That will clear the nth bit of number. You must invert the bit string with the bitwise NOT operator (~), then AND it.

这将清除 的n第 位number。您必须使用按位非运算符 ( ~)反转位字符串,然后使用 AND 它。

Toggling a bit

稍微切换

The XOR operator (^) can be used to toggle a bit.

XOR 运算符 ( ^) 可用于切换位。

number ^= 1UL << n;

That will toggle the nth bit of number.

这将切换 的n第 位number

Checking a bit

检查一点

You didn't ask for this, but I might as well add it.

你没有要求这个,但我不妨加上它。

To check a bit, shift the number n to the right, then bitwise AND it:

要检查一点,将数字 n 向右移动,然后按位与它:

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

That will put the value of the nth bit of numberinto the variable bit.

这会将 的n第 位的值number放入变量中bit

Changing the nth bit to x

将第n位更改为x

Setting the nth bit to either 1or 0can be achieved with the following on a 2's complement C++ implementation:

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

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

Bit nwill be set if xis 1, and cleared if xis 0. If xhas some other value, you get garbage. x = !!xwill booleanize it to 0 or 1.

n,如果将被设置x1,如果清除x0。如果x有一些其他价值,你会得到垃圾。 x = !!x将它布尔化为 0 或 1。

To make this independent of 2's complement negation behaviour (where -1has all bits set, unlike on a 1's complement or sign/magnitude C++ implementation), use unsigned negation.

要使其独立于 2 的补码否定行为(其中-1设置了所有位,与 1 的补码或符号/幅度 C++ 实现不同),请使用无符号否定。

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

or

或者

unsigned long newbit = !!x;    // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);

It's generally a good idea to use unsigned types for portable bit manipulation.

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

or

或者

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

(number & ~(1UL << n))will clear the nth bit and (x << n)will set the nth bit to x.

(number & ~(1UL << n))将清除nth 位并将 th 位(x << n)设置nx

It's also generally a good idea to not to copy/paste code in general and so many people use preprocessor macros (like the community wiki answer further down) or some sort of encapsulation.

一般来说,不要复制/粘贴代码也是一个好主意,因此很多人使用预处理器宏(如社区 wiki 的进一步回答)或某种封装。

回答by Martin York

Using the Standard C++ Library: std::bitset<N>.

使用标准 C++ 库:std::bitset<N>.

Or the Boostversion: boost::dynamic_bitset.

Boost版本:boost::dynamic_bitset.

There is no need to roll your own:

没有必要推出自己的:

#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

The Boost version allows a runtime sized bitset compared with a standard librarycompile-time sized bitset.

标准库编译时大小的位集相比,Boost 版本允许运行时大小的位集。

回答by Ferruccio

The other option is to use bit fields:

另一种选择是使用位域:

struct bits {
    unsigned int a:1;
    unsigned int b:1;
    unsigned int c:1;
};

struct bits mybits;

defines a 3-bit field (actually, it's three 1-bit felds). Bit operations now become a bit (haha) simpler:

定义一个 3 位字段(实际上,它是三个 1 位字段)。位操作现在变得有点(哈哈)更简单:

To set or clear a bit:

设置或清除一点:

mybits.b = 1;
mybits.c = 0;

To toggle a bit:

稍微切换一下:

mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1;  /* all work */

Checking a bit:

检查一点:

if (mybits.c)  //if mybits.c is non zero the next line below will execute

This only works with fixed-size bit fields. Otherwise you have to resort to the bit-twiddling techniques described in previous posts.

这仅适用于固定大小的位字段。否则,您必须求助于之前帖子中描述的位处理技术。

回答by Steve Karg

I use macros defined in a header file to handle bit set and clear:

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

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

回答by dmckee --- ex-moderator kitten

It is sometimes worth using an enumto namethe bits:

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

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

Then use the nameslater on. I.e. write

然后稍后使用这些名称。即写

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

to set, clear and test. This way you hide the magic numbers from the rest of your code.

设置,清除和测试。通过这种方式,您可以从其余代码中隐藏幻数。

Other than that I endorse Jeremy's solution.

除此之外,我赞同 Jeremy 的解决方案。

回答by yogeesh

From snip-c.zip's bitops.h:

来自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)))

OK, let's analyze things...

好吧,让我们来分析一下……

The common expression that you seem to be having problems with in all of these is "(1L << (posn))". All this does is create a mask with a single bit on and which will work with any integer type. The "posn" argument specifies the position where you want the bit. If posn==0, then this expression will evaluate to:

您在所有这些中似乎都有问题的常见表达是“(1L << (posn))”。所有这些都是创建一个带有单个位的掩码,该掩码适用于任何整数类型。“posn”参数指定您想要该位的位置。如果 posn==0,则此表达式的计算结果为:

0000 0000 0000 0000 0000 0000 0000 0001 binary.

If posn==8, it will evaluate to:

如果 posn==8,它将评估为:

0000 0000 0000 0000 0000 0001 0000 0000 binary.

In other words, it simply creates a field of 0's with a 1 at the specified position. The only tricky part is in the BitClr() macro where we need to set a single 0 bit in a field of 1's. This is accomplished by using the 1's complement of the same expression as denoted by the tilde (~) operator.

换句话说,它只是在指定位置创建一个 0 的字段和 1。唯一棘手的部分是在 BitClr() 宏中,我们需要在 1 的字段中设置单个 0 位。这是通过使用波浪号 (~) 运算符表示的相同表达式的 1 的补码来实现的。

Once the mask is created it's applied to the argument just as you suggest, by use of the bitwise and (&), or (|), and xor (^) operators. Since the mask is of type long, the macros will work just as well on char's, short's, int's, or long's.

创建掩码后,它会按照您的建议应用于参数,使用按位和 (&)、或 (|) 和 xor (^) 运算符。由于掩码是 long 类型,宏在 char、short、int 或 long 上也能正常工作。

The bottom line is that this is a general solution to an entire class of problems. It is, of course, possible and even appropriate to rewrite the equivalent of any of these macros with explicit mask values every time you need one, but why do it? Remember, the macro substitution occurs in the preprocessor and so the generated code will reflect the fact that the values are considered constant by the compiler - i.e. it's just as efficient to use the generalized macros as to "reinvent the wheel" every time you need to do bit manipulation.

最重要的是,这是对整类问题的通用解决方案。当然,每次需要时用显式掩码值重写这些宏的等效项是可能的,甚至是合适的,但为什么要这样做呢?请记住,宏替换发生在预处理器中,因此生成的代码将反映这样一个事实,即编译器认为这些值是常量 - 即每次需要时使用通用宏与“重新发明轮子”一样有效进行位操作。

Unconvinced? Here's some test code - I used Watcom C with full optimization and without using _cdecl so the resulting disassembly would be as clean as possible:

不服气?这是一些测试代码 - 我使用了经过全面优化的 Watcom C 并且没有使用 _cdecl,因此最终的反汇编会尽可能干净:

----[ TEST.C ]----------------------------------------------------------------

----[ 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) ]-----------------------------------------------

----[ 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 ]-----------------------------------------------------------------

----[ finis ]------------------------------------------- ---------------

回答by nsanders

Use the bitwise operators: &|

使用位运算符: &|

To set last bit in 000b:

设置最后一位000b

foo = foo | 001b

To check last bit in foo:

要检查最后一位foo

if ( foo & 001b ) ....

To clear last bit in foo:

要清除 中的最后一位foo

foo = foo & 110b

I used XXXbfor clarity. You'll probably be working with HEX representation, depending on the data structure in which you're packing bits.

XXXb为了清楚起见,我使用了。您可能会使用十六进制表示,具体取决于您在其中打包位的数据结构。

回答by kapilddit

For the beginner I would like to explain a bit more with an example:

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

Example:

例子:

value is 0x55;
bitnum : 3rd.

The &operator is used check the bit:

&运算符用于检查一下:

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)

Toggle or Flip:

切换或翻转:

0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)

|operator: set the bit

|运算符:设置位

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

回答by R.. GitHub STOP HELPING ICE

Here's my favorite bit arithmetic macro, which works for any type of unsigned integer array from unsigned charup to size_t(which is the biggest type that should be efficient to work with):

这里是我最喜欢的位算术宏,这对于任何类型的无符号整数数组的作品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)))))

To set a bit:

设置一点:

BITOP(array, bit, |=);

To clear a bit:

清除一点:

BITOP(array, bit, &=~);

To toggle a bit:

稍微切换一下:

BITOP(array, bit, ^=);

To test a bit:

测试一下:

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

etc.

等等。

回答by John U

As this is tagged "embedded" I'll assume you're using a microcontroller. All of the above suggestions are valid & work (read-modify-write, unions, structs, etc.).

由于这被标记为“嵌入式”,因此我假设您使用的是微控制器。以上所有建议都是有效且有效的(读-修改-写、联合、结构等)。

However, during a bout of oscilloscope-based debugging I was amazed to find that these methods have a considerable overhead in CPU cycles compared to writing a value directly to the micro's PORTnSET / PORTnCLEAR registers which makes a real difference where there are tight loops / high-frequency ISR's toggling pins.

然而,在基于示波器的调试过程中,我惊讶地发现这些方法在 CPU 周期中具有相当大的开销,与直接将值写入微的 PORTnSET / PORTnCLEAR 寄存器相比,这在存在紧密循环/高的情况下产生了真正的不同-频率 ISR 的切换引脚。

For those unfamiliar: In my example, the micro has a general pin-state register PORTn which reflects the output pins, so doing PORTn |= BIT_TO_SET results in a read-modify-write to that register. However, the PORTnSET / PORTnCLEAR registers take a '1' to mean "please make this bit 1" (SET) or "please make this bit zero" (CLEAR) and a '0' to mean "leave the pin alone". so, you end up with two port addresses depending whether you're setting or clearing the bit (not always convenient) but a muchfaster reaction and smaller assembled code.

对于那些不熟悉的人:在我的示例中,微控制器有一个通用引脚状态寄存器 PORTn,它反映了输出引脚,因此执行 PORTn |= BIT_TO_SET 会导致对该寄存器的读取-修改-写入。但是,PORTnSET / PORTnCLEAR 寄存器取“1”表示“请将此位设为 1”(SET)或“请将此位设为 0”(CLEAR),取“0”表示“不要管该引脚”。所以,你最终有两个端口地址取决于是否你设置或清除该位(并不总是很方便),但很多更快的反应和更小的汇编代码。