异或运算

const MaxUintptr = ^uintptr(0)

// MaxUint64 = 1<<64 - 1 有必要对比一下 math 包下的常量声明,这两个值大小相等

首先需要解释 ^ 的符号含义,表示按位异或。但按位异或操作的是两个数,这里 Go 的写法显然不是。符号代表什么含义呢?答案是 Go 没有专门的一元取反运算符,取而代之的便是 ^。它不仅可以作为按位异或的运算符,还可以作为一元取反运算符。

关于异或运算符,在知乎上有一个比较有趣的描述,虽然有点偏离主题,但重在有趣:“男人和女人能生出孩子”。访问如何理解「异或」的含义?查看详情。

那么 uintptr 这是什么类型呢?直观的感觉就是一个指针。在源码中,只能追到类型的定义:

// uintptr is an integer type that is large enough to hold the bit pattern of
// any pointer.
type uintptr uintptr

这个解释还是不能让我满足,但苦于没有思路去探究底层,文章Go Slice 最大容量大小是怎么来的对 uintptr 这样做了介绍,uintptr 的真面目如下:

#ifdef _64BIT
typedef    uint64        uintptr;
#else
typedef    uint32        uintptr;
#endif

虽然不知道这个解释的真实出处,但解释应该是正确的。uintptr 根据 32/64 位系统不同而不同。我的本机是 64 位系统,对无符号整数 0 取反后的二进制结果:64 个bit 位都是 1

我们接着来看 Go 源码中的一个常量声明:

// unsafe.Sizeof(uintptr(0)) but an ideal const
const PtrSize = 4 << (^uintptr(0) >> 63)

在解释这行代码的时候,我们需要首先了解一下左移和右移运算符,运算的表达式:运算 expression1 左移或右移 expression2 指定的位数。

expression1 (<<|>>) expression2

我们可以计算得出,在 64 位系统上 PtrSize 是 8 ,在 32 位系统上的结果是 4。我们可以猜测,这个常量是根据系统类型来初始化的。拿 64 系统来说,0 取反之后右移 63 位的结果是 1,然后 4 的二进制表示是 0100,左移 1 位的结果是 1000,结果为 8。

有了上述的基础之后,我们看 Go 源码中相关的一个方法声明,做一个乘法运算:

// MulUintptr returns a * b and whether the multiplication overflowed.
// On supported platforms this is an intrinsic lowered by the compiler.
func MulUintptr(a, b uintptr) (uintptr, bool) {
	if a|b < 1<<(4*sys.PtrSize) || a == 0 {
		return a * b, false
	}
	overflow := b > MaxUintptr/a
	return a * b, overflow
}

这里首次出现了 | 的位运算,双目运算符,含义是按位或运算。那么 a|b 是什么含义呢?如果 a==b,那么 a|b=a,这一步的操作究竟是什么含义呢?

我们先看 overflow,它作为一个布尔值直接返回,表达式是 b > MaxUintptr/a,这个表达式很明显是用来判断 a、b 相乘是否会导致结果越界。该怎么理解呢?MaxUintptr/a 这个表达式的结果最大其实应该是 a,也就是说,极限情况下 a 的平方等于 MaxUintptr。如果此时 b > MaxUintptr/a 的计算结果,必然会导致结果越界。侧面反映出,a、b中至少有一个值是大于 MaxUintptr 的平方根的。

我们在来看 if 这个表达式,结合if 里面 return 的写法,我们可以推测这个条件是用来限定 a、b 一定小于 MaxUintptr 的平方根逻辑。但是为什么表达式只判断了 a==0 而没有判断 b 呢?整体来看这个函数就会明白,因为在计算 overflow 的时候被除数是 a ,所以才在 if 条件中进行了判断。

最后,就只剩下 a|b < 1<<(4/*sys.PtrSize) 这个表达式了,sys.PtrSize 我们在上面已经解释过了, 1<<(4/*sys.PtrSize) 在 64 位系统上的含义是 1 左移 32 位,而 MaxUintptr 的数值大小为 1 左移 64 位减去 1,可以把 (4/*sys.PtrSize) 当作 MaxUintptr 的平方根。如果 a|b 都小于 MaxUintptr 的平方根,那么它们相乘的结果一定小于 MaxUintptr,说白了,就是在判断 a 和 b 是否都小于 MaxUintptr 的平方根

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页