这几天在研究 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. MIPI CSI-2 像素打包格式解析

    背景 MIPI CSI-2支持YUV.RGB和RAW data三种数据格式,这里是个笼统的叫法,具体又根据不同的像素打包方式细分为具体的格式,打包是什么概念?就是把Sensor采样得到的RGB三个通道 ...

  2. java的基本类型和类型转化

    1.八种基本数据类型: byte-short--int --long--float--double--char 2.八种基本类型所占字节空间 一.int 整数的直接量为int: 存储范围为:- -21 ...

  3. mysql 获取当前时间和时间戳

    mysql 获取当前时间为select now()运行结果: 2012-09-05 17:24:15 mysql 获取当前时间戳为select unix_timestamp(now()) 运行结果:u ...

  4. 如何取消 UIView 动画?

    原文链接 最近项目中有一个需求是需要手动点击相机对焦,这里由于相机对焦部分需要一个类似于系统对焦框一样的缩放动画,同时动画时长为0.3秒,因此这里就有一个很普遍的需求,如果用户在0.3秒内继续点击对焦 ...

  5. LGP5312题解

    压 位 T r i e 入 门 练 习 题(确信) 题意很清楚( 让我们先来想一想,如果没有排序操作的话,这道题应该怎么做. 我们维护一个 \(x\) 表示从开始到现在一共异或上了 \(x\),在序列 ...

  6. linux的服务自动启动的配置

    1.开机启动时自动运行程序 Linux加载后, 它将初始化硬件和设备驱动, 然后运行第一个进程init.init根据配置文件继续引导过程,启动其它进程.通常情况下,修改放置在 /etc/rc或 /et ...

  7. 西门子S210电机位置控制过调问题解决方法

    问题描述 创建完工艺对象,使用MC_MoveAbsolute工艺指令进行绝对定位,发现在下达指令后,电机会出现先超过目标位置再回调的现象,即过冲. 电机连接的机械结构为旋转轴,而不是线性轴. 解决方法 ...

  8. Apache Ranger安装部署

    1.概述 Apache Ranger提供了一个集中式的安全管理框架,用户可以通过操作Ranger Admin页面来配置各种策略,从而实现对Hadoop生成组件,比如HDFS.YARN.Hive.HBa ...

  9. Java基础 (上)

    基础概念与常识 Java 语言有哪些特点? 简单易学: 面向对象(封装,继承,多态): 平台无关性( Java 虚拟机实现平台无关性): 支持多线程( C++ 语言没有内置的多线程机制,因此必须调用操 ...

  10. 什么是 Hystrix?它如何实现容错?

    Hystrix 是一个延迟和容错库,旨在隔离远程系统,服务和第三方库的访问点,当出现故障是不可避免的故障时,停止级联故障并在复杂的分布式系统中实现弹性.通常对于使用微服 构开发的系统,涉及到许多微服务 ...