位运算符的真实世界使用案例

280

以下是一些与实际应用有关的位运算符:

  • AND(&)
  • XOR(^)
  • NOT(~)
  • OR(|)
  • 左/右移位(<< / >>)

1
我还记得当我第一次了解它们时,似乎它们只用于低级编程...但自那以后,它们已经进入我的工具箱,我经常使用它们,包括在进行“高级”编程时。现在它们对我来说就像+和*一样。 - Oak
2
@Anon:在我看来,现实世界应该指的是除了位运算符最明显的低级编程之外的任何东西。 - Olivier Lalonde
位运算的实际应用 - phuclv
可以使用二进制异或运算在排列中找到缺失的数字:https://www.martinkysel.com/codility-permmissingelem-solution/ - Janac Meena
41个回答

3
如果您想计算某个数字对2的某个幂次取模,可以使用yourNumber & 2^N-1,在这种情况下与yourNumber % 2^N相同。请注意保留HTML标记。
number % 16 = number & 15;
number % 128 = number & 127;

这可能只有在被除数非常大且为2^N时才有用,作为模运算的替代方案...但即使如此,在我的.NET 2.0测试中,它比模运算的速度提升微不足道。我怀疑现代编译器已经执行了像这样的优化。有人对此了解更多吗?


1
编译器确实使用这种优化...但如果你不知道这种优化,你可能会选择一个不是2的幂的除数。 - Ben Voigt
1
这取决于情况。在C#中,这实际上会产生不同的结果,因为“%”是余数运算,它们对待负数的方式不同。但是,如果您将uint传递给,则当第二个参数是预先已知的2的幂时,C#编译器实际上会生成使用按位AND的机器代码。 - Aaron Franke

2
当您只想更改微控制器输出的某些位,但要写入的寄存器是字节时,可以执行以下操作(伪代码):
char newOut = OutRegister & 0b00011111 //clear 3 msb's
newOut = newOut | 0b10100000 //write '101' to the 3 msb's
OutRegister = newOut //Update Outputs

当然,许多微控制器都允许您逐个更改每个位...

这应该是什么编程语言? - Carson Myers
@Carson:这不是任何一种编程语言,这只是伪代码。几年前我实际上用的是汇编语言,但我想用C语言也很容易实现。感谢提醒,我会更新以使其更清晰明了。 - Emilio M Bumachar
我编辑了答案,更改了注释,使高亮显示不那么混乱。我看到了,我以为它可能是C语言,但你使用了0b...符号表示,我希望在C语言中也有这种表示方式。 - Carson Myers

2

我使用它们来进行多选选项,这样我只需要存储一个值而不是10个或更多的值。


2

在SQL关系模型中,它也可以很方便。假设你有以下表格:BlogEntry、BlogCategory。

传统上,你可以使用BlogEntryCategory表格之间创建一个n-n的关系,或者当BlogCategory记录不多时,你可以像使用标记枚举一样,在BlogEntry中使用一个值来链接多个BlogCategory记录。

在大多数关系数据库管理系统中,还有非常快速的运算符可以选择该“已标记”列...


1

它们主要用于位运算(惊喜)。以下是在PHP代码库中找到的一些实际示例。

字符编码:

if (s <= 0 && (c & ~MBFL_WCSPLANE_MASK) == MBFL_WCSPLANE_KOI8R) {

数据结构:

ar_flags = other->ar_flags & ~SPL_ARRAY_INT_MASK;

数据库驱动程序:

dbh->transaction_flags &= ~(PDO_TRANS_ACCESS_MODE^PDO_TRANS_READONLY);

编译器实现:

opline->extended_value = (opline->extended_value & ~ZEND_FETCH_CLASS_MASK) | ZEND_FETCH_CLASS_INTERFACE;

1

我曾经看到它们被用于基于角色的访问控制系统。


1

2
不,它们并不相同。在C语言中,如果x == 1y == 2,那么x || y的值为1,而x | y的值为0。我也不明白为什么x^true!x更好。它需要更多的输入、不太地道,如果x恰好不是一个bool类型,它也是不可靠的。 - David Thornley
哦,等等...是的,这是我愚蠢的一面...今天我似乎无法清晰地思考。 - Earlz
x | y 等于3(编辑:算了,我看到你正在参考某些被编辑掉的内容!) - Pod
1
@DavidThornley:x^true 优于 !x 的一种情况是 some->complicated().member->lookup ^= true;。没有一元运算符的复合赋值版本。 - Ben Voigt

1
我经常使用位运算将多个选项的组合存储在一个整数中。
int options = 0;

其中OPTION1可能被定义为1,OPTION2为2,OPTION3为4,OPTION4为8,OPTION5为16,等等。

void addOption(int option)将使用|运算符将选项添加到选项中。

boolean hasOption(int option)将使用&运算符在选项中测试该选项。


1

我的问题在实际世界中有一个真正的用途 -
如何仅响应第一个WM_KEYDOWN通知?

在使用Windows C API消耗WM_KEYDOWN消息时,位30指定了先前的键状态。如果在发送消息之前按下了键,则该值为1,否则为零。


1

我在一些游戏开发书籍中看到过这种方法,它可以更高效地进行乘除运算。

2 << 3 == 2 * 8 
32 >> 4 == 32 / 16

4
这些书一定很旧了。 - Jimmy
1
由于编译器或处理器的改进,它不再更高效吗? - Seth Reno
1
由于处理器的改进,这种做法不再一定更有效率。特别是超标量CPU,更是如此,如果CPU实现了乱序执行。但对于小型微控制器来说,这仍然是一种有用的惯用语,尽管我使用过的所有编译器都会在关闭优化时自动执行此操作。 - slebetman
1
我想补充一下,它取的是除法的“地板”值。5>>1==2,而不是2.5:00000101->00000010。 - vol7ron
现代编译器在给定适当限制的情况下,会将二的幂次方的除法/乘法转换为 >><<(并将模数转换为 &&)。 - Aaron Franke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接