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


当前回答

设置一位

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

number |= 1UL << n;

这将设置数字的第n位。如果要将第1位设置为n-1,那么n应该为零,如果要设置第n位。

如果数字大于无符号长,则使用1ULL;1UL<<n的提升直到评估1UL<<n之后才发生,其中移动超过长的宽度是未定义的行为。这同样适用于所有其他示例。

清除一点

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

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

这将清除数字的第n位。必须使用按位NOT运算符(~)反转位字符串,然后对其进行AND运算。

稍微扭动一下

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

number ^= 1UL << n;

这将切换数字的第n位。

检查一点

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

要检查一位,请向右移动数字n,然后按位“与”:

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

这将把数字第n位的值放入变量位。

将第n位更改为x

在2的补码C++实现中,可以通过以下方式将第n位设置为1或0:

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

如果x为1,则设置位n,如果x为0,则清除位n。如果x有其他值,则会得到垃圾。x=!!x将其布尔化为0或1。

要使其独立于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);

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

or

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

(number&~(1UL<<n))将清除第n位,(x<<n)将第n位设置为x。

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

其他回答

设置一位

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

number |= 1UL << n;

这将设置数字的第n位。如果要将第1位设置为n-1,那么n应该为零,如果要设置第n位。

如果数字大于无符号长,则使用1ULL;1UL<<n的提升直到评估1UL<<n之后才发生,其中移动超过长的宽度是未定义的行为。这同样适用于所有其他示例。

清除一点

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

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

这将清除数字的第n位。必须使用按位NOT运算符(~)反转位字符串,然后对其进行AND运算。

稍微扭动一下

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

number ^= 1UL << n;

这将切换数字的第n位。

检查一点

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

要检查一位,请向右移动数字n,然后按位“与”:

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

这将把数字第n位的值放入变量位。

将第n位更改为x

在2的补码C++实现中,可以通过以下方式将第n位设置为1或0:

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

如果x为1,则设置位n,如果x为0,则清除位n。如果x有其他值,则会得到垃圾。x=!!x将其布尔化为0或1。

要使其独立于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);

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

or

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

(number&~(1UL<<n))将清除第n位,(x<<n)将第n位设置为x。

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

展开位集答案:

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

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

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

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

模板版本(放在头文件中),支持更改多位(适用于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输出。

这是我最喜欢的位算术宏,它适用于从无符号字符到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, &)) ...

etc.