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


当前回答

模板版本(放在头文件中),支持更改多位(适用于AVR微控制器btw):

namespace bit {
  template <typename T1, typename T2>
  constexpr inline T1 bitmask(T2 bit) 
  {return (T1)1 << bit;}
  template <typename T1, typename T3, typename ...T2>
  constexpr inline T1 bitmask(T3 bit, T2 ...bits) 
  {return ((T1)1 << bit) | bitmask<T1>(bits...);}

  /** Set these bits (others retain their state) */
  template <typename T1, typename ...T2>
  constexpr inline void set (T1 &variable, T2 ...bits) 
  {variable |= bitmask<T1>(bits...);}
  /** Set only these bits (others will be cleared) */
  template <typename T1, typename ...T2>
  constexpr inline void setOnly (T1 &variable, T2 ...bits) 
  {variable = bitmask<T1>(bits...);}
  /** Clear these bits (others retain their state) */
  template <typename T1, typename ...T2>
  constexpr inline void clear (T1 &variable, T2 ...bits) 
  {variable &= ~bitmask<T1>(bits...);}
  /** Flip these bits (others retain their state) */
  template <typename T1, typename ...T2>
  constexpr inline void flip (T1 &variable, T2 ...bits) 
  {variable ^= bitmask<T1>(bits...);}
  /** Check if any of these bits are set */
  template <typename T1, typename ...T2>
  constexpr inline bool isAnySet(const T1 &variable, T2 ...bits) 
  {return variable & bitmask<T1>(bits...);}
  /** Check if all these bits are set */
  template <typename T1, typename ...T2>
  constexpr inline bool isSet (const T1 &variable, T2 ...bits) 
  {return ((variable & bitmask<T1>(bits...)) == bitmask<T1>(bits...));}
  /** Check if all these bits are not set */
  template <typename T1, typename ...T2>
  constexpr inline bool isNotSet (const T1 &variable, T2 ...bits) 
  {return ((variable & bitmask<T1>(bits...)) != bitmask<T1>(bits...));}
}

使用示例:

#include <iostream>
#include <bitset> // for console output of binary values

// and include the code above of course

using namespace std;

int main() {
  uint8_t v = 0b1111'1100;
  bit::set(v, 0);
  cout << bitset<8>(v) << endl;

  bit::clear(v, 0,1);
  cout << bitset<8>(v) << endl;

  bit::flip(v, 0,1);
  cout << bitset<8>(v) << endl;

  bit::clear(v, 0,1,2,3,4,5,6,7);
  cout << bitset<8>(v) << endl;

  bit::flip(v, 0,7);
  cout << bitset<8>(v) << endl;
}

BTW:如果不向编译器发送优化器参数(例如:-O3),则不使用constexpr和inline。请随时尝试以下代码https://godbolt.org/并查看ASM输出。

其他回答

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

要解决试图形成掩码时的常见编码陷阱,请执行以下操作:1并不总是足够宽

当数字的类型大于1时会出现什么问题?x对于移位1<<x可能太大,导致未定义的行为(UB)。即使x不太大,~也可能无法翻转足够多的最高有效位。

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

要确保1足够宽:

代码可以使用1ull或pedatically(uintmax_t)1并让编译器进行优化。

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

或铸造-这使得编码/审查/维护问题保持铸造正确和最新。

number |= (type_of_number)1 << x;

或者通过强制一个至少与数字类型一样宽的数学运算来温和地推广1。

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

与大多数位操作一样,最好使用无符号类型而不是有符号类型

使用此项:

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

    return num;
}

从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

----[完成]-----------------------------------------------------------------

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

另一个选项是使用位字段:

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

这仅适用于固定大小的位字段。否则,您必须求助于前面文章中描述的比特旋转技术。