这几天在研究 Go 的源码,突然发现了一个之前没有见过的位运算,见这里

new &^= mutexWoken

&^,分别表示 AND 和 XOR,这个不用多说。

值得一提的是 ^ 这个符号,在我的印象中,它一直是一个二元运算符,平时见的最多的是 a ^ b 这种用法。

但是实际上它还是一个一元运算符。单走一个 a 也是没问题的,例如 ^a

^ 作为一元运算符的作用

去知识的源头寻找答案!

在 Go 的规范文档Constant expressions 这一节中有提到 ^ 作为一元运算符的作用

The mask used by the unary bitwise complement operator ^ matches the rule for non-constants: the mask is all 1s for unsigned constants and -1 for signed and untyped constants.

^1         // untyped integer constant, equal to -2
uint8(^1) // illegal: same as uint8(-2), -2 cannot be represented as a uint8
^uint8(1) // typed uint8 constant, same as 0xFF ^ uint8(1) = uint8(0xFE)
int8(^1) // same as int8(-2)
^int8(1) // same as -1 ^ int8(1) = -2

^a: 当 a 是 unsigned 时,相当于用 11111... (... 表示很多很多 1) 与 a 做异或运算;当 a 是 signed 时,相当于用 -1与 a 做异或运算

PS: 无论 int 还是 uint ,其底层都是用 bit 表示的,而 -1 用补码表示就是 1111... ,如果从 bit 的角度出现,可以发现,无论 a 是正数还是负数最终都是与 1111... 做 XOR 运算!而最终的效果则是将 a 所有的 bit 位的值全部反转

让我们来推导一下,首先复习一下反码和补码的知识:

反码: 正数的反码等于本身,负数的反码保持符号位不变,其他位取反

补码: 正数的补码等于它本身,负数的补码等于它的反码加一

下面为了表示方便,正数和负数用到 8 位 bit 表示,即 int8 和 uint8 。

类型 原码 反码 补码
int8 1 0000 0001 0000 0001 0000 0001
int8 -1 1000 0001 1111 1110 1111
uint8 1 0000 0001 0000 0001 0000 0001
int8 -2 1000 0010 1101 1111 1110
uint8 254 (255 - 1) 1111 1110 1111 1110 1111 1110
^1         // untyped integer constant,使用 -1 与其做 XOR 运算 1111 1111 ^ 0000 0001 = 1111 1110, 恰好为 -2 的补码
uint8(^1) // illegal: same as uint8(-2), -2 cannot be represented as a uint8
^uint8(1) // typed uint8 constant, same as 0xFF ^ uint8(1) = uint8(0xFE)
int8(^1) // same as int8(-2)
^int8(1) // same as -1 ^ int8(1) = -2
&^ 的作用

回到 &^ 上面来,在 Arithmetic_operators 这一节中记录了 &^ 这个运算符。

&^   bit clear (AND NOT)    integers

实际上 a &^ b 的效果近似于 a & (^b),即将 ^b 的结果与 a 做 AND 运算。

&^ 名为 bit clear,他的作用自然也就是 bit clear,a &^ mask 会将 a 中一些位置的 bit 值 clear 为 0,通过将 mask 中 bit 值设置为 1 来指定位置。

例如

pos    12345
a = 11001
mask = 01010

mask 的第 2 和第 4 位的 bit 为 1,则意味着将 a 的第 2 位和第 4 位的 bit clear 为 0,因此 a &^ mask 的结果为 10001

证明过程也很简单,之前说过 ^mask 的结果是将 mask 的 bit 位全部取反,所以 mask 内原本为 1 的 bit 就变成了 0,之后再与 a 做 AND 运算,任何 bit 值与 0 做 AND 运算的结果都为 0。

a &^ ba & ^b

上面提到过 a &^ b 的效果近似于 a & (^b),但是它两还是有一点微笑区别的。具体可见 stackoverflow 的这个问题: Why does Go have a "bit clear (AND NOT)" operator?

There's a subtle difference that makes dealing with literals and untyped constants easier with the explicit bit clear operator.

Untyped integers have their default type as int so something like a := uint32(1) & ^1 is illegal as ^1 is evaluated first and it's evaluated as ^int(1), which equals -2. a := uint32(1) &^ 1 is legal however as here 1 is evaluated as uint32, based on the context.

There could also be some performance gains in having an explicit bit clear, but I'm not too sure about that.

感触

哭,用了快两年的 Go,居然连 Go 的语法都还没学完!

随机推荐

  1. LeetCode-024-两两交换链表中的节点

    两两交换链表中的节点 题目描述:给定一个链表,两两交换其中相邻的节点,并返回交换后的链表. 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换. 示例说明请见LeetCode官网. 来源:力 ...

  2. LOTO示波器汽修专用款选型指南

    LOTO示波器汽修专用款选型指南 LOTO各种型号的示波器其实都可以用作汽车传感器信号波形的检测.汽修应用中,工程师对示波器的性能要求对于LOTO产品来说不算高. 在我们销售和技术支持的积累过程中,我 ...

  3. 微信小程序token失效 自动请求后端,适用于自动登录

    app.js // app.js App({ onLaunch() { let token = wx.getStorageSync('token') if (!token) { wx.login({ ...

  4. 学习Java集合

    1.列表  List接口(继承于Collection接口)及其实现类 List接口及其实现类是容量可变的列表,可按索引访问集合中的元素. 特点:集合中的元素有序.可重复: 列表在数据结构中分别表现为: ...

  5. linux 配置redis密码

    1.打开redis配置文件 vi /usr/local/redis/etc/redis.conf 添加requirepass 密码    将bind 127.0.0.1 ::1前的#去掉 保存退出 2 ...

  6. React学习小结(一)

    一.React的发展 facebook在构建instagram网站的时候遇见两个问题: 1.数据绑定的时候,大量操作真实dom,性能成本太高 2.网站的数据流向太混乱,不好控制 于是facebook起 ...

  7. netty系列之:netty中各不同种类的channel详解

    目录 简介 ServerChannel和它的类型 Epoll和Kqueue AbstractServerChannel ServerSocketChannel ServerDomainSocketCh ...

  8. 2、mysql如何控制用户对数据库的访问

    基础理解:通过对用户赋予某些权限就可以控制用户对数据库的访问 更深层次的理解:当mysql对用户赋予某些权限时,mysql底层是如何控制用户对数据库的访问 用户管理和权限管理 (基础理解) 用户管理 ...

  9. ROS第一次课作业分享

    ROS第一次课作业分享 2021年夏季学期学院开设了ROS的相关课程,最近在复习相关知识,正好做一下整理.下面是第一次作业的要求: 编写一个ROS节点,具备以下功能: 读取小海龟仿真器的/turtle ...

  10. JDBC快速入门(附Java通过jar包连接MySQL数据库)

    •通过jar包连接mysql数据库 •下载jar包 Java 连接 MySQL 需要驱动包,官网下载地址为MySQL驱动包官网下载,选择适合的jar包版本进行安装 (记得安装的地址,下面导入包时会用到 ...