代码之家  ›  专栏  ›  技术社区  ›  Kent Fredric

位运算符的实际用例[关闭]

  •  192
  • Kent Fredric  · 技术社区  · 5 年前

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

    • 异或
    • 不是
    42 回复  |  直到 7 年前
        1
  •  192
  •   Chris de Vries    14 年前
    • 位字段(标志)
      它们是表示由几个“是或否”属性定义的状态的最有效方法。acls是一个很好的例子;如果您有4个离散的权限(读、写、执行、更改策略),最好将其存储在1字节,而不是浪费4字节。为了增加便利性,可以用多种语言将它们映射到枚举类型。

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

    • 压缩,加密
      这两种算法都严重依赖于位算法。看看 deflate 例如,算法——一切都是以位为单位的,而不是字节。

    • 有限状态机
      我说的主要是嵌入在一些硬件中的那种,尽管它们也可以在软件中找到。它们本质上是组合的——它们可能从字面上被“编译”成一堆逻辑门,因此它们必须表示为 AND , OR , NOT 等。

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

    这些只是我提出的前几个例子——这并不是一个详尽的清单。

        2
  •  41
  •   Aaron Novstrup    14 年前

    奇怪吗?

    (value & 0x1) > 0
    

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

    (value & 0x1) == 0
    
        3
  •  23
  •   Carl Norum    7 年前

    低级编程就是一个很好的例子。例如,您可能需要将一个特定的位写入内存映射寄存器,以使某个硬件按您希望的方式工作:

    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() 使用 & | 操作员(在 endianness (字节顺序)与网络顺序不匹配:

    #define htons(a) ((((a) & 0xff00) >> 8) | \
                      (((a) & 0x00ff) << 8))
    
    #define htonl(a) ((((a) & 0xff000000) >> 24) | \
                      (((a) & 0x00ff0000) >>  8) | \
                      (((a) & 0x0000ff00) <<  8) | \
                      (((a) & 0x000000ff) << 24))
    
        4
  •  22
  •   nos    7 年前

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

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

    设置收费标志:

    flags |= Chargeable;
    

    清除CallerID缺少标志:

    flags &= ~CallerIDMissing;
    

    测试是否设置了CallerID Missing和Charged:

    if((flags & (CallerIDMissing | Chargeable )) == (CallerIDMissing | Chargeable)) {
    
    }
    
        5
  •  20
  •   Terje    14 年前

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

        6
  •  18
  •   JonoW    14 年前

    我已经使用位操作来实现CMS的安全模型。它有页面,如果用户在适当的组中,他们可以访问这些页面。一个用户可能在多个组中,因此我们需要检查用户组和页面组之间是否存在交叉点。因此,我们为每个组分配了一个唯一的2次幂标识符,例如:

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

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

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

        7
  •  15
  •   André    7 年前

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

    我用位和把它们取出来。例如:

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

    等。

        8
  •  10
  •   recursive    14 年前

    加密是所有按位操作。

        9
  •  10
  •   Thorsten S.    14 年前

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

    转换:
    要将较短的变量转换为具有位标识的较长变量,需要调整位,因为int中的-1是0xffffffff,而long中的-1是0xffffffffffffffffff。保存 转换后应用遮罩的标识。

    =或
    设置位。如果已经设置了位,则位将被单独设置。许多数据结构(位域)都有如下标志:is_hset=0,is_vset=1,可以独立设置。 要设置标志,请应用is_hset_is_vset(在c和assembly中,这非常方便阅读)

    ^ =异或
    查找相同或不同的位。

    ~=不
    翻转位。

    可以看出 全部的 这些操作可以实现可能的本地位操作。 因此,如果您愿意,可以只通过位操作实现一条加法指令。

    一些很棒的黑客:

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

        10
  •  8
  •   ezod    14 年前

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

        11
  •  8
  •   ChaosPandion    14 年前

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

    int a = 1230123;
    int b = 1234555;
    int c = 5865683;
    int hash = a ^ b ^ c;
    
        12
  •  6
  •   DrDol    14 年前

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

    1字节变量

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

    特别是轮班操作员(<<>)通常用于计算。

        13
  •  5
  •   Buhake Sindi    14 年前

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

    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.
    

    我希望这个小小的例子有帮助…

        14
  •  5
  •   James Cooper    7 年前

    在当今现代语言的抽象世界里,不算太多。文件IO是一个容易想到的问题,尽管它正在对已经实现的东西执行位操作,而不是实现使用位操作的东西。不过,作为一个简单的示例,此代码演示如何在C中删除文件的只读属性(以便它可以与指定filemode.create的新文件流一起使用):

    //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
        }
    

    你看到了吗?通过使用“inbox”枚举值inboundmemos(&)进行anding,我知道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;
        }
    

    非常简单,但在抽象层次上的整洁实现通常不需要按位操作。

        15
  •  4
  •   rayd09    14 年前

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

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

        16
  •  4
  •   bill    14 年前

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

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

        17
  •  3
  •   Jason Williams    14 年前

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

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

        18
  •  3
  •   user3552161    7 年前

    位运算符对于循环长度为2次方的数组很有用。正如许多人所提到的,按位运算符非常有用,并且在 旗帜 , 绘图 , 网络 , 加密 . 不仅如此,而且速度非常快。我个人最喜欢的用途是 一个 数组 没有 条件句 . 假设你有一个 基于零索引 数组(例如,第一个元素的索引是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的幂,那么使用按位运算符可以轻松生成类似 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);
    }
    

    这就是它的工作原理。在 二元的 格式每一个2的幂减去1的数字只能用1来表示。例如,二进制中的3是 11 7是 111 15是 1111 等等,你明白了。现在,如果你 & 对一个只有一个二进制数的数有什么区别吗?假设我们这样做:

    num & 7;
    

    如果 num 小于或等于7,则结果为 号码 因为每一位 & -带1的ed本身就是。如果 号码 大于7,在 & 操作计算机将考虑7的前导零,当然,这些零在 & 操作仅保留尾随部分。就好像万一 9 & 7 在二进制文件中,它看起来像

    1001 & 0111
    

    结果将是0.001,它是十进制的1,并且在数组中寻址第二个元素。

        19
  •  3
  •   user3382203    7 年前

    通常,位运算比乘法/除法快。所以,如果你需要用变量x乘以9,你会做到的。 x<<3 + x x*9 .如果此代码在ISR中,您将节省响应时间。

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

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

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

        20
  •  2
  •   SQLMenace    14 年前

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

        21
  •  2
  •   Tim Mahy    14 年前

    它在SQL关系模型中也很方便,假设您有以下表:blogentry、blogcategory

    传统上,您可以使用BlogentryCategory表在它们之间创建N-N关系。 或者,当没有太多BlogCategory记录时,可以使用BlogEntry中的一个值链接到多个BlogCategory记录,就像处理标记的枚举一样, 在大多数RDBMS中,在“标记”列上也有一个非常快速的运算符来选择…

        22
  •  2
  •   Dimitris Andreou    14 年前

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

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

    整数的最高位 X ?(例如,这可用于查找大于 X )

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

    哪个最低 1 整数的位 X ?(帮助查找可被2除尽的次数。)

    x & -x
    
        23
  •  2
  •   Jonas Lincoln    14 年前

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

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

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

        24
  •  2
  •   Dan7    14 年前

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

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

    这可能只是一个有用的替代模量运算非常大的股息是2^n…但即使这样,在我在.NET 2.0上的测试中,它在模数运算上的速度提升也可以忽略不计。我怀疑现代编译器已经执行了这样的优化。有人知道更多吗?

        25
  •  1
  •   ScottE    14 年前

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

        26
  •  1
  •   ScottG    7 年前

    在我的问题中有一个现实世界的用法-
    response to only the first wm\u keydown notification?

    在使用WindowsC API位30中的wm_keydown消息时,指定以前的密钥状态。如果在发送消息之前键是向下的,则值为1;如果键是向上的,则值为零。

    Respond to only the first WM_KEYDOWN notification?

    在使用WindowsC API位30中的wm_keydown消息时,指定以前的密钥状态。如果在发送消息之前键是向下的,则值为1;如果键是向上的,则值为零。

        27
  •  1
  •   Constantin    14 年前

    它们主要用于按位运算(惊喜)。下面是一些在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;
    
        28
  •  1
  •   Simon    14 年前

    每当我第一次启动C编程时,我都理解了真值表和所有这些,但直到我阅读本文,它才真正地使用它。 http://www.gamedev.net/reference/articles/article1563.asp (给出了现实生活的例子)

        29
  •  1
  •   Tim Snowhite    14 年前

    我不认为这算是按位的,但是Ruby的数组通过普通的整数按位运算符定义了set操作。所以 [1,2,4] & [1,2,3] # => [1,2] . 同样地 a ^ b #=> set difference a | b #=> union .

        30
  •  1
  •   Dungeon Hunter    7 年前

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

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

    可以找到此解决方案的解释 here