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 的语法都还没学完!
随机推荐
- 前端工程化:使用 shelljs 生成 yapi 接口文件
之前的文章介绍了使用 yapi-to-typescript (下文简称 ytt)生成接口类型定义文件,方便我们直接使用接口的请求和响应类型,能减少很多写接口类型的时间. 使用 yapi-to-type ...
- jQuery Validatede 结合Ajax 表单验证提交
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Linux服务器 I/O 原理和流程
计算机I/OI/O在计算机中指Input/Output,IOPS (Input/Output Per Second)即每秒的输入输出量(或读写次数),是衡量磁盘性能的主要指标之一.IOPS是指单位时间 ...
- Nebula Graph 在企查查的应用
本文首发于 Nebula Graph Community 公众号 背景 企查查是企查查科技有限公司旗下的一款企业信用查询工具,旨在为用户提供快速查询企业工商信息.法院判决信息.关联企业信息.法律诉讼. ...
- CVE-2017-8759 微软word漏洞复现以及利用
CVE-2017-8759 微软word漏洞复现以及利用 0x00 漏洞描述 近日,360集团核心安全事业部分析团队发现一个新型的Office文档高级威胁攻击,攻击使用了9月12日补丁刚修复的.NET ...
- 如何用系统命令sc修改系统服务
系统服务是系统的一项重要组成部分,sc命令在特定权限下,可以实现对指定服务的启动项.配置文件及状态进行修改,位置在c:\windows\system32\目录下面,下面就和大家分享一下如何用系统命令s ...
- 三层PetShop架构设计
<解剖 PetShop >系列之一 前言: PetShop 是一个范例,微软用它来展示 .Net 企业系统开发的能力.业界有许多 .Net 与 J2EE 之争,许多数据是从微软的 Pe ...
- vs2017连接sqlsever数据库
vs2017连接mysql数据库操作步骤 怎样使用vs2017连接数据库 [C++]VS2015/VS2017连接Mysql数据库教程
- HashMap 链表和红黑树的转换
HashMap在jdk1.8之后引入了红黑树的概念,表示若桶中链表元素超过8时,会自动转化成红黑树:若桶中元素小于等于6时,树结构还原成链表形式. 原因: 红黑树的平均查找长度是log(n),长度为8 ...
- kafka分布式的情况下,如何保证消息的顺序?
作者:可期链接:https://www.zhihu.com/question/266390197/answer/772404605来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注 ...