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 的语法都还没学完!
随机推荐
- LeetCode-043-字符串相乘
字符串相乘 题目描述:给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式. 示例说明请见LeetCode官网. 来源:力扣( ...
- 修饰符-final
Java是由C/C++泛生的,其也保留了C/C++的部分特性,如关键字.在C/C++中,关键字有着特殊的含义. final修饰符 在编程中,一般会存在一些变量或方法,程序员不让其数据"发生改 ...
- GateWay配置使用
是什么 Cloud全家桶中有个很重要的组建就是网关,在1.x版本中都是采用的Zuul网关 但在2.X版本中,zuul的升级一直跳票,SpringCloud最后自己研发了一个网关替代Zuul Sprin ...
- Linux备份数据库,mysqldump命令实例详解
mysqldump是mysql数据库中备份工具,用于将MYSQL服务器中的数据库以标准的sql语言的方式导出,并保存到文件中. 语法: mysqldump (选项) 选项: --add-drop-ta ...
- 痞子衡嵌入式:MCUBootUtility v3.5发布,支持串行NOR的ECC及双程序启动
-- 痞子衡维护的 NXP-MCUBootUtility 工具距离上一个大版本(v3.4.0)发布过去半年了,这一次痞子衡为大家带来了版本升级 v3.5.0,这个版本主要有几个非常重要的更新需要跟大家 ...
- IIS 虚拟目录 VS 站点
1.虚拟目录是站点的一个子目录,虚拟目录是建立在站点下面的. 2.一个站点下面,可以建立多个虚拟目录.各个虚拟目录都是以虚拟目录名称来进行区分. 3.新建的独立站点就和IIS中的默认站点一样,各个站点 ...
- 040_Eureka 服务注册与发现
目录 什么是Eureka 原理讲解 Eureka的基本架构 三大角色 盘点目前工程状况 创建Eureka服务端子模块 springcloud-eureka-7001 导入依赖 pom.xml 配置文件 ...
- VMware15安装 CentOS7 步骤
- Mybatis中的基本对象的生命周期和作用域
不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题 SqlSessionFactoryBuilder 这个类可以被实例化.使用和丢弃,一旦创建了 SqlSessionFact ...
- k8s集群关机后,如何解决 kubernetes 重启起不来的问题
如何解决 kubernetes 重启后,启来不来的问题 登录自己的Kubernetes测试集群时发现集群好像没有启动成功 运行 kubectl get pods --all -A ,报错如下. 第一反 ...