Go 里面的 ^ 和 &^
这几天在研究 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 &^ b 与 a & ^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) & ^1is illegal as ^1 is evaluated first and it's evaluated as ^int(1), which equals -2.a := uint32(1) &^ 1is 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 的语法都还没学完!
随机推荐
- Python脚本生成可执行文件&(恋爱小脚本)
Python脚本生成可执行文件&(恋爱小脚本) 参考文献: http://c.biancheng.net/view/2690.html; https://blog.csdn.net/qq_39 ...
- selenium在爬虫中的使用
一. selenium概述 1.1 定义 Selenium是一个Web的自动化测试工具,最初是为网站自动化测试而开发的,Selenium 可以直接调用浏览器,它支持所有主流的浏览器(包括Phantom ...
- LeetCode-033-搜索旋转排序数组
搜索旋转排序数组 题目描述:整数数组 nums 按升序排列,数组中的值 互不相同 . 在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行 ...
- SQL Server--频繁建立连接和断开连接
使用数据库时,不建议一直与数据库保持连接,最好用时连接用完断开连接. 我的C#程序中采用"用时连接用完断开连接"的方式: 之前是C#程序调用本地数据库,没遇到问题: 后来改为C#程 ...
- out与err输出流
System.in.System.out与System.err 当我们查阅文档可知,out与err都是Java中的输出流,in是"标准"输入流,System.out是"标 ...
- 修饰符-final
Java是由C/C++泛生的,其也保留了C/C++的部分特性,如关键字.在C/C++中,关键字有着特殊的含义. final修饰符 在编程中,一般会存在一些变量或方法,程序员不让其数据"发生改 ...
- 微信小程序下滑时能实现加载更多数据
wxml代码: <view class="scroll"> <!-- 绑订页面上拉触底事件的处理函数onReachBottom事件 --> <scro ...
- cURL error 60: SSL certificate problem: unable to get local issuer certifica 解决
从 https://curl.haxx.se/docs/caextract.html 上下载cacert.pem 打开php.ini 搜索curl.cainfo 与 openssl.cafile,将 ...
- Windows下搭建REDIS集群
Redis集群: 如果部署到多台电脑,就跟普通的集群一样:因为Redis是单线程处理的,多核CPU也只能使用一个核, 所以部署在同一台电脑上,通过运行多个Redis实例组成集群,然后能提高CPU的利用 ...
- 线程的sleep()方法和yield()方法有什么区别?
sleep()方法和yield()方法的区别: sleep()方法给其他线程运行机会时,不考虑线程的优先级,因此会给低优先级的线程运行机会:yield()方法只会给相同优先级或更高优先级的线程运行机会 ...