位运算符的真实用例

下面的位运算符在现实世界中有哪些用例?

  • XOR
  • 左/右转
113402 次浏览

我将它们用于多选择选项,这样我只存储一个值,而不是10个或更多

低级编程就是一个很好的例子。例如,你可能需要写一个特定的位到内存映射寄存器,以使某些硬件做你想要它做的事情:

volatile uint32_t *register = (volatile uint32_t *)0x87000000;
uint32_t          value;
uint32_t          set_bit   = 0x00010000;
uint32_t          clear_bit = 0x00001000;


value = *register;            // get current value from the register
value = value & ~clear_bit;   // clear a bit
value = value | set_bit;      // set a bit
*register = value;            // write it back to the register

同样,htonl()htons()是使用&|操作符实现的(在字节顺序(字节顺序)不匹配网络顺序的机器上):

#define htons(a) ((((a) & 0xff00) >> 8) | \
(((a) & 0x00ff) << 8))


#define htonl(a) ((((a) & 0xff000000) >> 24) | \
(((a) & 0x00ff0000) >>  8) | \
(((a) & 0x0000ff00) <<  8) | \
(((a) & 0x000000ff) << 24))

大约三分钟前,我刚刚使用了位xor (^)来计算与PLC串行通信的校验和…

您可以使用它们作为一种快速而不常用的散列数据的方法。

int a = 1230123;
int b = 1234555;
int c = 5865683;
int hash = a ^ b ^ c;

例如,我使用它们从打包的颜色值中获取RGB(A)值。

加密都是按位操作。

Base64编码就是一个例子。Base64编码用于将二进制数据表示为通过电子邮件系统(和其他目的)发送的可打印字符。Base64编码将一系列8位字节转换为6位字符查找索引。位操作,移位,'ing, 'ing, not'ing对于实现Base64编码和解码所需的位操作非常有用。

当然,这只是无数例子中的一个。

位,用于屏蔽/提取字节的某一部分。

1字节变量

 01110010
&00001111 Bitmask of 0x0F to find out the lower nibble
--------
00000010

特别是移位算子(<<>>)通常用于计算。

当我有一堆布尔标记时,我喜欢将它们全部存储在一个整型中。

我用bitwise-AND取出它们。例如:

int flags;
if (flags & 0x10) {
// Turn this feature on.
}


if (flags & 0x08) {
// Turn a second feature on.
}

等。

当你只想改变微控制器输出的一些位,但要写入的寄存器是一个字节时,你可以这样做(伪代码):

char newOut = OutRegister & 0b00011111 //clear 3 msb's
newOut = newOut | 0b10100000 //write '101' to the 3 msb's
OutRegister = newOut //Update Outputs

当然,许多微控制器允许你单独改变每一位。

下面是一些处理将标志存储为单个位的常见习惯用法。

enum CDRIndicators {
Local = 1 << 0,
External = 1 << 1,
CallerIDMissing = 1 << 2,
Chargeable = 1 << 3
};


unsigned int flags = 0;

设置Chargeable标志:

flags |= Chargeable;

清除CallerIDMissing标记:

flags &= ~CallerIDMissing;

测试CallerIDMissing和Chargeable是否设置:

if((flags & (CallerIDMissing | Chargeable )) == (CallerIDMissing | Chargeable)) {


}

我见过它们在基于角色的访问控制系统中使用。

当我第一次开始C编程时,我理解了真值表和所有的东西,但直到我读到这篇文章http://www.gamedev.net/reference/articles/article1563.asp(给出了现实生活中的例子),我才知道如何实际使用它。

    <李> < p > 位字段(标志) < br > 它们是表示由几个“是或否”属性定义的状态的最有效方法。acl就是一个很好的例子;如果你有4个离散的权限(读、写、执行、更改策略),最好将其存储在1个字节中,而不是浪费4个字节。这些可以映射到许多语言中的枚举类型,以增加便利性 <李> < p > 通过端口/套接字进行通信 < br > 总是涉及校验和、奇偶校验、停止位、流控制算法等,这些算法通常取决于单个字节的逻辑值,而不是数值,因为介质可能一次只能传输一位 <李> < p > 压缩、加密 < br > 这两者都严重依赖于逐位算法。以缩小算法为例-所有内容都以比特为单位,而不是字节 <李> < p > 有限状态机 < br > 我主要说的是嵌入在硬件中的那种,尽管它们也可以在软件中找到。它们本质上是组合的——它们可能会被“编译”成一堆逻辑门,所以它们必须被表示为ANDORNOT,等等。

  • < p > < >强图形 这里几乎没有足够的空间来讨论图形编程中使用这些运算符的每个领域。XOR(或^)在这里特别有趣,因为第二次应用相同的输入将撤消第一次的输入。旧的gui过去常常依赖于此来进行选择高亮显示和其他覆盖,以消除昂贵的重绘需求。它们在慢速图形协议(即远程桌面)中仍然有用

这些只是我最先想到的几个例子——这不是一个详尽的清单。

我经常使用位操作将选项的组合存储在一个整数中。

int options = 0;

其中OPTION1可以定义为1,OPTION2为2,OPTION3为4,OPTION4为8,OPTION5为16,…

void addOption(int option)将使用|操作符向options中添加一个选项。

boolean hasOption(int option)将使用&操作符来测试选项中的选项。

我的问题在现实世界中有一个用途-
只响应第一个WM_KEYDOWN通知? < / > < / p >

当在windows C api中使用WM_KEYDOWN消息时,第30位指定前一个键的状态。如果在发送消息之前键为down,则值为1;如果键为up,则值为0

在当今现代语言的抽象世界里,没有太多。File IO是一个容易想到的方法,尽管它是在已经实现的东西上执行按位操作,而不是实现使用按位操作的东西。尽管如此,作为一个简单的例子,这段代码演示了在c#中删除文件上的只读属性(这样它就可以与指定FileMode.Create的新FileStream一起使用):

//Hidden files posses some extra attibutes that make the FileStream throw an exception
//even with FileMode.Create (if exists -> overwrite) so delete it and don't worry about it!
if(File.Exists(targetName))
{
FileAttributes attributes = File.GetAttributes(targetName);


if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
File.SetAttributes(targetName, attributes & (~FileAttributes.ReadOnly));


File.Delete(targetName);
}
至于自定义实现,这里有一个最近的例子: 我创建了一个“消息中心”,用于从分布式应用程序的一个安装发送安全消息到另一个安装。基本上,它类似于电子邮件,包括收件箱、发件箱、已发送等,但它也有读取收据的保证,因此在“收件箱”和“已发送”之外还有额外的子文件夹。这意味着我需要定义“收件箱中”或“已发送文件夹中”的内容。对于已发送的文件夹,我需要知道哪些已读,哪些未读。对于未读的内容,我需要知道收到了什么,没有收到什么。我使用这些信息来构建一个动态的where子句,该子句过滤本地数据源并显示适当的信息

下面是枚举是如何组合在一起的:

    public enum MemoView :int
{
InboundMemos = 1,                   //     0000 0001
InboundMemosForMyOrders = 3,        //     0000 0011
SentMemosAll = 16,                  //     0001 0000
SentMemosNotReceived = 48,          //     0011
SentMemosReceivedNotRead = 80,      //     0101
SentMemosRead = 144,                //     1001
Outbox = 272,                       //0001 0001 0000
OutBoxErrors = 784                  //0011 0001 0000
}

你明白这是怎么回事了吗?通过与“收件箱”枚举值InboundMemos加上(&),我知道InboundMemosForMyOrders在收件箱中。

下面是该方法的简化版本,它构建并返回为当前选择的文件夹定义视图的过滤器:

    private string GetFilterForView(MemoView view, DefaultableBoolean readOnly)
{
string filter = string.Empty;
if((view & MemoView.InboundMemos) == MemoView.InboundMemos)
{
filter = "<inbox filter conditions>";


if((view & MemoView.InboundMemosForMyOrders) == MemoView.InboundMemosForMyOrders)
{
filter += "<my memo filter conditions>";
}
}
else if((view & MemoView.SentMemosAll) == MemoView.SentMemosAll)
{
//all sent items have originating system = to local
filter = "<memos leaving current system>";


if((view & MemoView.Outbox) == MemoView.Outbox)
{
...
}
else
{
//sent sub folders
filter += "<all sent items>";


if((view & MemoView.SentMemosNotReceived) == MemoView.SentMemosNotReceived)
{
if((view & MemoView.SentMemosReceivedNotRead) == MemoView.SentMemosReceivedNotRead)
{
filter += "<not received and not read conditions>";
}
else
filter += "<received and not read conditions>";
}
}
}


return filter;
}

非常简单,但在抽象级别上是一个整洁的实现,通常不需要按位操作。

它们主要用于位操作(惊喜)。下面是在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;

我曾在一些游戏开发书籍中看到它是一种更有效的乘除方法。

2 << 3 == 2 * 8
32 >> 4 == 32 / 16
< p >,=和:< br >
.屏蔽特定位 您正在定义应该显示的特定位 或者不显示。0 x0,x将清除字节中的所有位,而0xFF不会改变x。

. 0x0F将显示下啃位 < p >转换:< br > 要将较短的变量转换为具有位标识的较长的变量,必须调整位,因为int类型中的-1是0xFFFFFFFF,而long类型中的-1是0xffffffffffffffffff。为了保护 转换后应用掩码的标识 < p > | =和< br > 位设置。如果已经设置了位,则位将独立设置。许多数据结构(位字段)有IS_HSET = 0, IS_VSET = 1这样的标志,可以独立设置。 要设置标志,您应用IS_HSET | IS_VSET(在C和汇编中,这非常方便阅读)

< p > ^ = XOR < br >

.查找相同或不同的位 < p > ~ =不是< br > 翻转位。< / p > 可以证明所有可能的本地位操作可以通过这些操作实现。 因此,如果你愿意,你可以只通过位操作来实现ADD指令

以下是一些妙招:

< p > http://www.ugcs.caltech.edu/~wnoise/base2.html < br > http://www.jjj.de/bitwizardry/bitwizardrypage.html < / p >

它在sql关系模型中也很方便,假设你有以下表:BlogEntry, BlogCategory

传统上,您可以使用BlogEntryCategory表在它们之间创建一个n-n关系 或者当没有那么多的BlogCategory记录时,你可以在BlogEntry中使用一个值来链接到多个BlogCategory记录,就像你会用标记的枚举做的那样, 在大多数RDBMS中,也有一个非常快速的操作符来选择'标记'列…

奇怪吗?

(value & 0x1) > 0

它能被2(偶数)整除吗?

(value & 0x1) == 0

一个数字x是2的幂吗?(例如,在计数器递增的算法中很有用,并且一个操作只执行对数次)

(x & (x - 1)) == 0

整数x的最高位是哪位?(例如,这可以用来找到比x大的2的最小次幂)

x |= (x >>  1);
x |= (x >>  2);
x |= (x >>  4);
x |= (x >>  8);
x |= (x >> 16);
return x - (x >>> 1); // ">>>" is unsigned right shift

整数x中最低的1位是哪位?(帮助找出能被2整除的次数。)

x & -x

我使用它们作为选项处理程序,例如在访问控制列表中描述特定的资源。

看看这篇文章http://planetozh.com/blog/2006/05/php-bitwise-operators-example-of-use/

编辑:

还有一个链接: http://blog.code-head.com/how-to-write-a-permission-system-using-bits-and-bitwise-operations-in-php < / p >

我写了一篇小的维基文章,展示了作者/读者二进制。它在位级上工作,并展示了如何使用位操作符来打包数据。这可能是一个“现实世界”的例子,因为它在游戏中也有应用。

我在为CMS实现安全模型时使用了按位操作。如果用户属于适当的组,就可以访问它的页面。一个用户可以在多个组中,因此我们需要检查用户组和页面组之间是否有交集。因此,我们为每个组分配了一个唯一的2次方标识符,例如:

Group A = 1 --> 00000001
Group B = 2 --> 00000010
Group C = 3 --> 00000100

我们将这些值一起OR,并将值(作为单个int)存储在页面中。例如,如果一个页面可以被a组访问&B,我们存储值3(二进制为00000011)作为页面访问控制。同样,我们将or组标识符的值存储给用户,以表示用户所在的组。

因此,要检查给定用户是否可以访问给定页面,只需将这些值与运算在一起,并检查该值是否非零。这是非常快的,因为这个检查是在一条指令中实现的,没有循环,没有数据库往返。

我一直假设按位操作是相当简单的操作,所以当运行时间至关重要时,通过bitset实现的解决方案可以通过恒定的数量提高运行时间,这取决于算法。

数据库世界中的另一个真实应用程序是MySQL,它的数据类型为

位操作符由DBMS存储SET数据类型。设置可以节省空间。

Element    SET Value    Decimal Value
Travel      00000001    1
Sports      00000010    2
Dancing    00000100    4
Fine Dining   00001000  8

我不认为这是按位计算的,但是ruby的Array通过普通整数按位操作符定义了集合操作。所以[1,2,4] & [1,2,3] # => [1,2]a ^ b #=> set differencea | b #=> union也是如此。

我使用它们来实现快速BCD计算(会计师和审计员会对fp舍入感到不安)。

我们使用位标记,使会话较小的登录权限在我们的内部网站。

我很惊讶,没有人为互联网时代选择一个显而易见的答案。计算子网的有效网络地址。

http://www.topwebhosts.org/tools/netmask.php

似乎没有人提到定点数学。

(是的,我老了,好吗?)

一个非常具体的例子,但我用它们让我的数独求解器运行得更快(我和一个朋友进行了比赛)

每一列、行和3x3都表示为一个无符号整数,当我设置数字时,我会为相关列、行和3x3平方中设置的数字标记适当的位。

这样就很容易看到我可以在给定的正方形中放置什么可能的数字,因为我将右边的列、行和3x3的正方形放在一起,然后不这样做,留下一个表示给定位置可能的合法值的掩码。

希望大家能理解。

这是一个从字节格式的位图图像中读取颜色的例子

byte imagePixel = 0xCCDDEE; /* Image in RRGGBB format R=Red, G=Green, B=Blue */


//To only have red
byte redColour = imagePixel & 0xFF0000; /*Bitmasking with AND operator */


//Now, we only want red colour
redColour = (redColour >> 24) & 0xFF;  /* This now returns a red colour between 0x00 and 0xFF.

我希望这个小例子可以帮助....

还没人提到过收藏。有时您有一个较小的可能值集合,比如只有10或20个可能值,您希望将其中一些值保存在一个集合中。当然,你可以使用常规的Set实现,它很可能会使用一个支持哈希表。但由于可能值的集合是如此之小,这实际上只是浪费时间和空间。相反,你可以将集合存储在一个intlong值中,如果我没记错的话,这正是java EnumSet所做的。

如果你想计算你的数字mod(%) 2的某次方,你可以使用yourNumber & 2^N-1,在这种情况下与yourNumber % 2^N相同。

number % 16 = number & 15;
number % 128 = number & 127;

这可能只是作为模数运算的一种替代品有用,它的红利很大,是2^N。但即便如此,在我在。net 2.0上的测试中,它相对于模运算的速度提升也可以忽略不计。我怀疑现代编译器已经执行了这样的优化。有人知道更多吗?

一个常见的用法是对齐,例如我需要我的数据在4字节或16字节的边界上对齐。这在RISC处理器中非常常见,其中未对齐的加载/存储要么代价高昂(因为它触发了一个异常处理程序,然后需要修复未对齐的加载),要么根本不允许。

对于任何以2为幂的对齐,下一个对齐的pos可以计算如下:

aligned_offset = alignment + ((current_offset - 1) & ~(alignment - 1))

所以在4字节对齐和当前偏移量为9的情况下:

aligned_offset = 4 + ((9-1) & ~(4-1)) = 4 + (8 & 0xFFFFFFFC) = 4+ 8  = 12

所以下一个4字节的对齐偏移量是12

河内塔线性解采用位运算来解决问题。

public static void linear(char start, char temp, char end, int discs)
{
int from,to;
for (int i = 1; i < (1 << discs); i++) {
from = (i & i-1) % 3;
to = ((i | i-1) + 1) % 3;
System.out.println(from+" => "+to);
}
}

这个解决方案的解释可以找到在这里

位运算符对于长度为2的次幂的循环数组非常有用。正如许多人提到的,位运算符非常有用,在旗帜图形网络加密中使用。不仅如此,它们还非常快。我个人最喜欢的用法是循环一个没有条件数组。假设你有一个zero-index基础数组(例如:第一个元素的索引是0),你需要无限循环它。无限是指从第一个元素到最后一个元素再返回到第一个元素。实现它的一种方法是:

int[] arr = new int[8];
int i = 0;
while (true) {
print(arr[i]);
i = i + 1;
if (i >= arr.length)
i = 0;
}

这是最简单的方法,如果你想避免如果语句,你可以像这样使用模量方法:

int[] arr = new int[8];
int i = 0;
while (true) {
print(arr[i]);
i = i + 1;
i = i % arr.length;
}

这两种方法的缺点是,模运算符是昂贵的,因为它在整数除法后寻找余数。第一个方法在每次迭代中运行如果语句。然而,如果数组的长度是2的幂,则可以使用&(位和)操作符,如i & length,轻松生成类似0 .. length - 1的序列。知道了这些,上面的代码就变成了

int[] arr = new int[8];
int i = 0;
while (true){
print(arr[i]);
i = i + 1;
i = i & (arr.length - 1);
}

下面是它的工作原理。在二进制格式中,每个2的幂减去1的数字都只能用1表示。例如,二进制中的3是11, 7是111, 15是1111,等等,你明白了吧。现在,如果你&任意数对一个仅由1组成的二进制数会发生什么?假设我们这样做:

num & 7;

如果num小于或等于7,则结果将是num,因为每个带有1的&-ed位就是它自己。如果num大于7,在&操作期间,计算机将考虑7的前导零,当然,在&操作后,这些前导零将保持为零,只有后面的部分将保留。就像二进制的9 & 7一样

1001 & 0111

结果将是0001,它是十进制的1,并定位数组中的第二个元素。

通常位运算比乘除运算快。因此,如果你需要将变量x乘以9,你将执行x<<3 + x,这将比x*9快几个周期。如果此代码位于ISR中,则可以节省响应时间。

类似地,如果您想使用数组作为循环队列,那么使用逐位操作来处理环绕检查会更快(也更优雅)。(你的数组大小应该是2的幂)。例如:,如果你想插入/删除,你可以使用tail = ((tail & MASK) + 1)而不是tail = ((tail +1) < size) ? tail+1 : 0

另外,如果您想要一个错误标志将多个错误代码保存在一起,则每个位可以保存一个单独的值。您可以与它与每个单独的错误代码作为检查。这用于Unix错误代码。

此外,n位位图可以是一个非常酷而紧凑的数据结构。如果要分配一个大小为n的资源池,我们可以使用n位表示当前状态。