按位运算符的真实世界用例

Oli*_*nde 217 language-agnostic bitwise-operators

以下按位运算符的一些实际用例是什么?

  • XOR
  • 要么

Aar*_*ght 210

  • 位字段(标志)
    它们是表示状态由几个"是或否"属性定义的事物的最有效方式.ACL就是一个很好的例子; 如果您说4个离散权限(读取,写入,执行,更改策略),最好将其存储在1个字节而不是浪费4.这些可以映射到多种语言的枚举类型,以增加方便性.

  • 通过端口/套接字进行通信
    始终涉及校验和,奇偶校验,停止位,流控制算法等,这些算法通常取决于单个字节的逻辑值而不是数值,因为介质可能只能传输一位一时间

  • 压缩,加密
    这两者都严重依赖于按位算法.查看deflate算法的示例 - 一切都是位,而不是字节.

  • 有限状态机
    我主要讲的是嵌入在某些硬件中的那种,尽管它们也可以在软件中找到.这些组合在本质上-他们可能从字面上可以得到"编译"到一堆逻辑门,所以他们必须表现为AND,OR,NOT,等.

  • 图形 这里几乎没有足够的空间进入图形编程中使用这些运算符的每个区域. XOR(或^)在这里特别有趣,因为第二次应用相同的输入将撤消第一次.较旧的GUI过去依赖于此选择突出显示和其他叠加,以消除昂贵的重绘需求.它们在慢速图形协议(即远程桌面)中仍然有用.

这些只是我提出的前几个例子 - 这不是一个详尽的清单.


Set*_*eth 46

这很奇怪吗?

(value & 0x1) > 0
Run Code Online (Sandbox Code Playgroud)

它可以被两个(偶数)整除吗?

(value & 0x1) == 0
Run Code Online (Sandbox Code Playgroud)

  • 根据您的语言值和0x1> 0可能会被解析为值&(0x1> 0) (3认同)
  • @leeeroy - 真的够了.添加了一些parens. (3认同)
  • 现代优化器会将 (value% 2) != 0 这样的表达式自动转换为上述表达式。https://godbolt.org/z/mYEBH4 (2认同)

Car*_*rum 24

低级编程就是一个很好的例子.您可能,例如,需要写一个特定位的存储器映射寄存器做出一些硬件的你希望它是什么:

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
Run Code Online (Sandbox Code Playgroud)

此外,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))
Run Code Online (Sandbox Code Playgroud)

  • `htons()`和`htonl()`是POSIX函数来交换一个`short`或从主机(`h`)字节序到网络(`N`)字节顺序一个`long`. (7认同)
  • 不是每个人都在机器上说话.你的第二个例子是做什么的? (6认同)

nos*_*nos 24

这里有一些常见的习惯用法处理存储为单个位的标志.

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

unsigned int flags = 0;
Run Code Online (Sandbox Code Playgroud)

设置Chargeable标志:

flags |= Chargeable;
Run Code Online (Sandbox Code Playgroud)

清除CallerIDMissing标志:

flags &= ~CallerIDMissing;
Run Code Online (Sandbox Code Playgroud)

测试是否设置了CallerIDMissing和Chargeable:

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

}
Run Code Online (Sandbox Code Playgroud)

  • +1用于设置枚举值的正确方法:-) (2认同)

Jon*_*noW 24

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

Group A = 1 --> 00000001
Group B = 2 --> 00000010
Group C = 3 --> 00000100
Run Code Online (Sandbox Code Playgroud)

我们将这些值组合在一起,并将值(作为单个int)存储在页面中.例如,如果组A和B可以访问页面,我们将值3(二进制为00000011)存储为页面访问控制.以同样的方式,我们将ORed组标识符的值存储到用户以表示它们所在的组.

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


Ter*_*rje 21

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

  • 在我的机器上,`(a&b)>> c`比`a%d/e'快5倍以上(两种方式从表示ARGB的int中提取单个颜色值).对于10亿次迭代,分别为6.7s和35.2s. (3认同)

Jam*_*nen 14

当我有一堆布尔标志时,我喜欢将它们全部存储在一个int中.

我用bitwise-AND把它们搞定了.例如:

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

if (flags & 0x08) {
  // Turn a second feature on.
}
Run Code Online (Sandbox Code Playgroud)

等等

  • 希望这些实际代码中的常量而不是幻数:) (23认同)
  • 我知道这个主题与语言无关,但是 C 提供了一种简单的方法来使用 [bit-fields](http://www.tutorialspoint.com/cprogramming/c_bit_fields.htm) 来做到这一点,所以你可以使用像 `if (flags .feature_one_is_one) { // 打开功能 }`. 它符合 ANSI C 标准,因此可移植性不应该成为问题。 (2认同)

rec*_*ive 11

加密是所有按位操作.

  • 真?加密实现可能使用按位运算,但加密算法通常以数字术语描述,而不是以位表示形式描述. (4认同)
  • 那么除了实现算法之外,你还能用算法做什么呢?我很好奇。 (2认同)
  • @Constantin:参见,例如,DES的是如何实现的描述:(http://en.wikipedia.org/wiki/Data_Encryption_Standard#The_Feistel_.28F.29_function (2认同)

Tho*_* S. 11

&= AND:
屏蔽掉特定位.
您正在定义应显示或不显示的特定位.0x0和x将清除一个字节中的所有位,而0xFF不会更改x.0x0F将显示低半字节中的位.

转换:
要将较短的变量转换为具有位标识的较长变量,必须调整位,因为int中的-1是0xFFFFFFFF而long中的-1是0xFFFFFFFFFFFFFFFF.要保留标识,请在转换后应用蒙版.

| = OR
设置位.如果它们已经设置,则将独立设置这些位.许多数据结构(位域)具有IS_HSET = 0,IS_VSET = 1等标志,可以独立设置.要设置标志,请应用IS_HSET | IS_VSET(在C和汇编中这非常方便阅读)

^ = XOR
查找相同或不同的位.

〜= NOT
翻转位.

可以证明,这些操作可以实现所有可能的本地位操作.因此,如果您愿意,您可以仅通过位操作实现ADD指令.

一些精彩的黑客:

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


Cha*_*ion 9

您可以使用它们作为哈希数据的快速和脏的方式.

int a = 1230123;
int b = 1234555;
int c = 5865683;
int hash = a ^ b ^ c;
Run Code Online (Sandbox Code Playgroud)


ezo*_*zod 8

我在^大约三分钟前使用了bitwise-XOR()来计算与PLC串行通信的校验和...


DrD*_*Dol 6

Bitwise&用于屏蔽/提取字节的某个部分.

1个字节变量

 01110010
&00001111 Bitmask of 0x0F to find out the lower nibble
 --------
 00000010
Run Code Online (Sandbox Code Playgroud)

特别是移位运算符(<< >>)通常用于计算.


Buh*_*ndi 6

这是以字节格式从位图图像中读取颜色的示例

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.
Run Code Online (Sandbox Code Playgroud)

我希望这些小例子能帮助....


小智 6

位运算符对于循环长度为 2 的幂的数组很有用。正如许多人提到的,位运算符非常有用,用于FlagsGraphicsNetworkingEncryption。不仅如此,它们的速度还非常快。我个人最喜欢的用途是在没有条件的情况循环数组。假设你有一个基于零索引的数组(例如第一个元素的索引是0)并且你需要无限循环它。我所说的无限期是指从第一个元素到最后一个元素,然后返回到第一个元素。实现这一点的一种方法是:

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

这是最简单的方法,如果您想避免if语句,您可以使用模数方法,如下所示:

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

这两种方法的缺点是模运算符的成本很高,因为它在整数除法后寻找余数。第一个方法在每次迭代时运行if语句。但是,使用按位运算符,如果数组的长度是 2 的幂,则可以0 .. length - 1通过使用&(按位与) 运算符轻松生成序列,如下所示i & length。所以知道了这一点,上面的代码就变成了

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

下面是它的工作原理。在二进制格式中,每个 2 的幂除以 1 的数字仅用 1 表示。例如,二进制中的 3 是11, 7 是111, 15 是1111等等,您就明白了。&现在,如果将任何数字与仅由二进制组成的数字相比较,会发生什么?假设我们这样做:

num & 7;
Run Code Online (Sandbox Code Playgroud)

如果num小于或等于 7,则结果将是num因为每个加&1 的位都是其本身。如果num大于7,&运算时计算机会考虑7的前导零,当然&运算后仍保留零,仅保留尾部部分。就像二进制的情况一样,9 & 7它看起来像

1001 & 0111
Run Code Online (Sandbox Code Playgroud)

结果将为 0001,即十进制 1,并寻址数组中的第二个元素。


Fre*_*red 5

在今天现代语言的抽象世界中,并不是太多.文件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);
}
Run Code Online (Sandbox Code Playgroud)

至于自定义实现,这是最近的一个例子:我创建了一个"消息中心",用于将安全消息从我们的分布式应用程序的一个安装发送到另一个.基本上,它类似于电子邮件,包括收件箱,发件箱,已发送等,但它也保证了带有阅读回执的交付,因此除"收件箱"和"已发送"之外还有其他子文件夹.这就是我要求一般定义什么是"在收件箱中"或"在发送文件夹中"的内容.在已发送的文件夹中,我需要知道读取的内容和未读的内容.在未读的内容中,我需要知道收到的内容和收到的内容.我使用此信息构建一个动态where子句,用于过滤本地数据源并显示相应的信息.

以下是enum的组合方式:

    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
    }
Run Code Online (Sandbox Code Playgroud)

你知道这是做什么的吗?通过使用"收件箱"枚举值(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;
    }
Run Code Online (Sandbox Code Playgroud)

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


Jas*_*ams 5

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

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


use*_*203 5

通常按位运算比乘法/除法要快。因此,如果您需要将变量 x 乘以 9,那么您将比x<<3 + x快几个周期x*9。如果此代码在 ISR 内,您将节省响应时间。

同样,如果您想将数组用作循环队列,则使用位操作来处理环绕检查会更快(也更优雅)。(您的数组大小应该是 2 的幂)。例如: ,如果要插入/删除,则可以使用tail = ((tail & MASK) + 1)代替tail = ((tail +1) < size) ? tail+1 : 0

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

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