【发布时间】:2023-02-14 20:26:03
【问题描述】:
如何设置、清除和切换位?
【问题讨论】:
-
这就引出了一个问题,即多位的规范问题是什么。
标签: c++ c bit-manipulation bitwise-operators
如何设置、清除和切换位?
【问题讨论】:
标签: c++ c bit-manipulation bitwise-operators
使用按位或运算符 (|) 设置位。
number |= 1UL << n;
这将设置 number 的 nth 位。 n 应该为零,如果你想设置1st 位等等直到n-1,如果你想设置nth 位。
如果number比unsigned long宽,则使用1ULL; 1UL << n 的提升直到评估 1UL << n 之后才会发生,其中移动超过 long 的宽度是未定义的行为。这同样适用于所有其余示例。
使用按位与运算符 (&) 清除位。
number &= ~(1UL << n);
这将清除 number 的 nth 位。您必须使用按位 NOT 运算符 (~) 反转位串,然后将其与。
XOR 运算符 (^) 可用于切换位。
number ^= 1UL << n;
这将切换 number 的 nth 位。
你没有要求这个,但我不妨添加它。
要检查一点,将数字 n 向右移动,然后按位与它:
bit = (number >> n) & 1U;
这会将number的nth位的值放入变量bit中。
将 nth 位设置为 1 或 0 可以通过以下 2 的补码 C++ 实现来实现:
number ^= (-x ^ number) & (1UL << n);
如果x为1,位n将被设置,如果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)) 将清除nth 位,(x << n) 将nth 位设置为x。
一般来说,不要复制/粘贴代码也是一个好主意,所以很多人使用预处理器宏(如the community wiki answer further down)或某种封装。
【讨论】:
bit = (number >> x) & 1
1 是一个 int 字面值,已签名。所以这里的所有操作都对有符号数进行操作,标准没有很好地定义它。该标准不保证补码或算术移位,因此最好使用1U。
number = number & ~(1 << n) | (x << n); 将第 n 位更改为 x。
使用标准 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
与 standard library 编译时大小的位集相比,Boost 版本允许运行时大小的位集。
【讨论】:
另一种选择是使用位字段:
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
#define BITMASK_SET(x, mask) ((x) |= (mask))
#define BITMASK_CLEAR(x, mask) ((x) &= (~(mask)))
#define BITMASK_FLIP(x, mask) ((x) ^= (mask))
#define BITMASK_CHECK_ALL(x, mask) (!(~(x) & (mask)))
#define BITMASK_CHECK_ANY(x, mask) ((x) & (mask))
【讨论】:
BITMASK_CHECK(x,y) ((x) & (y)) 必须是 ((x) & (y)) == (y) 否则它会在多位掩码上返回不正确的结果(例如 5 与 3)/*所有掘墓人你好:)*/
1 应该是 (uintmax_t)1 或类似的,以防有人试图在 long 或更大的类型上使用这些宏
BITMASK_CHECK_ALL(x,y)可以实现为!~((~(y))|(x))
!(~(x) & (y)) 之后,更容易理解为什么它会起作用
有时值得使用 enum 来姓名位:
enum ThingFlags = {
ThingMask = 0x0000,
ThingFlag0 = 1 << 0,
ThingFlag1 = 1 << 1,
ThingError = 1 << 8,
}
然后使用名字稍后的。 IE。写
thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}
设置,清除和测试。通过这种方式,您可以从其余代码中隐藏幻数。
除此之外,我支持Paige Ruten's solution。
【讨论】:
clearbits() 函数而不是 &= ~。为什么要为此使用枚举?我认为那些是为了创建一堆具有隐藏的任意值的唯一变量,但你正在为每个变量分配一个确定的值。那么与仅仅将它们定义为变量相比有什么好处呢?
enums 用于相关常量集的使用在 C 编程中可以追溯到很久以前。我怀疑对于现代编译器而言,与const short 或其他任何东西相比,唯一的优势是它们被明确地组合在一起。当你想要他们做某事时其他比位掩码你得到自动编号。当然,在 C++ 中,它们也形成不同的类型,这为您提供了一些额外的静态错误检查。
ThingError|ThingFlag1 的 enum ThingFlags 值是多少?
int。由于对有符号类型进行隐式整数提升或按位运算,这可能会导致各种细微的错误。 thingstate = ThingFlag1 >> 1 例如将调用实现定义的行为。 thingstate = (ThingFlag1 >> x) << y 可以调用未定义的行为。等等。为了安全起见,请始终转换为无符号类型。
enum My16Bits: unsigned short { ... };
/*
** 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 来实现的 波浪号 (~) 运算符表示的相同表达式的补码。
创建掩码后,它会按照您的建议应用于参数, 通过使用按位与 (&)、或 (|) 和异或 (^) 运算符。自从面具 是 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;
}
----[ TEST.OUT(反汇编)]------------------------------------ ----------
Module: C:BINK st.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
----[结束]---------------------------------------- ----------------------
【讨论】:
arg 是long long,这将失败。 1L 需要尽可能宽的类型,所以 (uintmax_t)1 。 (你可能会逃脱1ull)
对于初学者,我想用一个例子来解释一下:
例子:
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)
【讨论】:
由于这被标记为“嵌入式”,我假设您使用的是微控制器。以上所有建议均有效且有效(读-修改-写、联合、结构等)。
然而,在一次基于示波器的调试过程中,我惊讶地发现,与将值直接写入微控制器的 PORTnSET / PORTnCLEAR 寄存器相比,这些方法在 CPU 周期上有相当大的开销,这在有紧密循环/高的地方产生了真正的区别-频率 ISR 的切换引脚。
对于那些不熟悉的人:在我的示例中,微处理器有一个通用引脚状态寄存器 PORTn,它反映了输出引脚,因此执行 PORTn |= BIT_TO_SET 会导致对该寄存器进行读取-修改-写入。但是,PORTnSET / PORTnCLEAR 寄存器采用“1”表示“请将此位设为 1”(SET) 或“请将此位设为零”(CLEAR),采用“0”表示“保留引脚”。因此,您最终会得到两个端口地址,具体取决于您是设置还是清除该位(并不总是很方便),但是很多更快的反应和更小的汇编代码。
【讨论】:
volatile,因此编译器无法对涉及此类寄存器的代码执行任何优化。因此,反汇编此类代码并查看其在汇编程序级别的结果是一种很好的做法。
这是我最喜欢的位算术宏,它适用于从 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, &)) ...
ETC。
【讨论】:
BITOP(array, bit++, |=); 很可能不会做调用者想要的。
BITCELL(a,b) |= BITMASK(a,b);(两者都将 a 作为参数来确定大小,但是后者永远不会评估 a,因为它只出现在 sizeof 中)。
(size_t)演员似乎只是为了确保一些无符号数学与%。可以 (unsigned) 那里。
(size_t)(b)/(8*sizeof *(a)) 在除法之前可能会不必要地缩小 b。只有非常大的位数组才有问题。仍然是一个有趣的宏。
让我们先假设几件事
num = 55执行按位操作的整数(设置、获取、清除、切换)。n = 4基于 0 的位位置来执行按位运算。如何获得一点?
- 要获取 num 的
nth位右移num,n次。然后用 1 执行按位与&。bit = (num >> n) & 1;怎么运行的?
0011 0111 (55 in decimal) >> 4 (right shift 4 times) ----------------- 0000 0011 & 0000 0001 (1 in decimal) ----------------- => 0000 0001 (final result)如何设置位?
- 设置数字的特定位。左移 1
n次。然后对num进行按位或|运算。num |= (1 << n); // Equivalent to; num = (1 << n) | num;怎么运行的?
0000 0001 (1 in decimal) << 4 (left shift 4 times) ----------------- 0001 0000 | 0011 0111 (55 in decimal) ----------------- => 0001 0000 (final result)怎么清一点?
- 左移 1,
n次,即1 << n。- 对上述结果进行位补。这样第 n 位变为未设置,其余位变为设置,即
~ (1 << n)。- 最后,将上述结果与
num进行按位与&运算。以上三步合起来可以写成num & (~ (1 << n));num &= (~(1 << n)); // Equivalent to; num = num & (~(1 << n));怎么运行的?
0000 0001 (1 in decimal) << 4 (left shift 4 times) ----------------- ~ 0001 0000 ----------------- 1110 1111 & 0011 0111 (55 in decimal) ----------------- => 0010 0111 (final result)如何切换位?
为了切换位,我们使用按位 XOR
^运算符。如果两个操作数的相应位不同,则按位异或运算符的计算结果为 1,否则计算结果为 0。也就是说要翻转一个位,我们需要将要翻转的位和1进行异或运算。
num ^= (1 << n); // Equivalent to; num = num ^ (1 << n);怎么运行的?
- 如果要切换的位为 0,则
0 ^ 1 => 1。- 如果要切换的位为 1,则
1 ^ 1 => 0。0000 0001 (1 in decimal) << 4 (left shift 4 times) ----------------- 0001 0000 ^ 0011 0111 (55 in decimal) ----------------- => 0010 0111 (final result)
【讨论】:
位域方法在嵌入式领域还有其他优势。您可以定义一个直接映射到特定硬件寄存器中的位的结构。
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
", 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)))
【讨论】:
CHAR_BIT 已经由limits.h 定义,你不需要输入你自己的BITS(事实上你这样做会让你的代码变得更糟)
该程序是将任何数据位从 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
",bit);
if (bitvalue == 0)
{
if (bit == 0)
printf("%x
", data);
else
{
data = (data^(invbitvalue<<bitpos));
printf("%x
", data);
}
}
else
{
if (bit == 1)
printf("elseif %x
", data);
else
{
data = (data|(bitvalue<<bitpos));
printf("else %x
", data);
}
}
}
【讨论】:
如果你正在做很多小事,你可能想使用面具,这将使整个事情更快。以下函数速度非常快,而且仍然很灵活(它们允许在任何大小的位图中进行位旋转)。
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);
由您来确保位数在您传递的位图的范围内。请注意,对于字节、字、双字、四字等在内存中相互正确映射的小端处理器(小端处理器比大端处理器“更好”的主要原因,啊,我觉得一场激烈的战争即将来临在...)。
【讨论】:
用这个:
int ToggleNthBit ( unsigned char n, int num )
{
if(num & (1 << n))
num &= ~(1 << n);
else
num |= (1 << n);
return num;
}
【讨论】:
扩展 bitset 答案:
#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;
}
【讨论】:
如果你想用 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 计算机上也很有用 保持处理器之间的一致性。
【讨论】:
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 个值。
我曾经使用这种方法查找重复的贷款记录,其中 loan_number 是 ISAM 键,使用 6 位数字的贷款号作为位数组的索引。速度非常快,8 个月后,证明我们从中获取数据的大型机系统实际上出现了故障。位数组的简单性使人们对其正确性的信心非常高——与搜索方法相比。
【讨论】:
bool 至少使用一个完整字节的存储空间。对于使用 int 实现 bool 的 C89 设置,甚至可能需要 4 个字节
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);
}
【讨论】:
check_nth_bit的返回类型可以是bool。
这是我使用的一些宏:
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)
【讨论】:
如何设置、清除和切换单个位?
要解决尝试形成掩码时的常见编码陷阱:
1并不总是足够宽当
number是比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或迂腐地使用(uintmax_t)1并让编译器优化。number |= (1ull << x); number |= ((uintmax_t)1 << x);或转换 - 这会导致编码/审查/维护问题,从而使转换保持正确和最新。
number |= (type_of_number)1 << x;或者通过强制至少与
number类型一样宽的数学运算来温和地提升1。number |= (number*0 + 1) << x;
与大多数位操作一样,最好使用未签名类型而不是签那些
【讨论】:
number |= (type_of_number)1 << x; 和number |= (number*0 + 1) << x; 都不适合设置有符号类型的符号位...事实上,number |= (1ull << x); 也不合适。有没有一种可移植的方式来按位置进行操作?
该程序基于@Jeremy 的上述解决方案。如果有人想快点玩。
public class BitwiseOperations {
public static void main(String args[]) {
setABit(0, 4); // set the 4th bit, 0000 -> 1000 [8]
clearABit(16, 5); // clear the 5th bit, 10000 -> 00000 [0]
toggleABit(8, 4); // toggle the 4th bit, 1000 -> 0000 [0]
checkABit(8,4); // check the 4th bit 1000 -> true
}
public static void setABit(int input, int n) {
input = input | ( 1 << n-1);
System.out.println(input);
}
public static void clearABit(int input, int n) {
input = input & ~(1 << n-1);
System.out.println(input);
}
public static void toggleABit(int input, int n) {
input = input ^ (1 << n-1);
System.out.println(input);
}
public static void checkABit(int input, int n) {
boolean isSet = ((input >> n-1) & 1) == 1;
System.out.println(isSet);
}
}
Output :
8
0
0
true
【讨论】:
支持更改多个位的模板化版本(放在头文件中)(顺便说一句,适用于 AVR 微控制器):
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;
}
顺便说一句:事实证明,如果不向编译器发送优化器参数(例如:-O3),则不会使用 constexpr 和 inline。请随意尝试https://godbolt.org/ 的代码并查看 ASM 输出。
【讨论】:
;?)
(variable & bits == bits)?
((variable & bits) == bits)
std::bitset
下面是 C 中执行基本位运算的例程:
#define INT_BIT (unsigned int) (sizeof(unsigned int) * 8U) //number of bits in unsigned int
int main(void)
{
unsigned int k = 5; //k is the bit position; here it is the 5th bit from the LSb (0th bit)
unsigned int regA = 0x00007C7C; //we perform bitwise operations on regA
regA |= (1U << k); //Set kth bit
regA &= ~(1U << k); //Clear kth bit
regA ^= (1U << k); //Toggle kth bit
regA = (regA << k) | regA >> (INT_BIT - k); //Rotate left by k bits
regA = (regA >> k) | regA << (INT_BIT - k); //Rotate right by k bits
return 0;
}
【讨论】:
有时当您不确定 -1 或类似的结果会导致什么时,您可能希望设置第 n 位而不使用 -1:
number = (((number | (1 << n)) ^ (1 << n))) | (x << n);
解释:((number | (1 << n) 将第 n 位设置为 1(其中 | 表示按位或),然后使用 (...) ^ (1 << n) 将第 n 位设置为 0,最后使用 (...) | x << n) 将第 n 位设置为 0, (位值)x。
这也适用于golang。
【讨论】:
(number & ~(1 << n)) | (!!x << n)。
尝试使用 C 语言中的这些函数之一来更改 n 位:
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;
}
【讨论】:
value << n 可能会导致未定义的行为
1 更改为 0x1 或 1UL 以避免 UB @M.M 正在谈论