当你写的时候
1<<64
The 1
上面不是一个int64
。它是一个常量文字。从语言规范来看:
常量表达式总是被精确计算;中间值
并且常数本身可能需要很大的精度
大于该语言中任何预先声明的类型所支持的。
因此,常量文字是在编译时计算的,并且可能非常大,因为它不是语言实现的特定类型。
下面实际上会给出溢出错误:
var i int64
i = 1<<65 - 1
因为现在常量文字表达式的计算结果大于int64
可以包含。
阅读更多相关内容here https://golang.org/ref/spec#Constant_expressions.
了解您的示例代码为何有效i = 65
,参考以下Golang规范specs https://golang.org/ref/spec#Operators:
移位表达式中的右操作数必须是无符号整数
类型或者是可以转换为无符号的无类型常量
整数类型。如果非常量移位表达式的左操作数
是一个无类型常量,它首先被转换为它想要的类型
假设移位表达式是否被其左操作数替换
独自的.
上面的粗体部分涉及您的代码。考虑下面的代码:
a := 66
var j uint64 = 1<<uint64(a) - 1
在移位运算符中,右操作数是非常量表达式。所以整个移位操作就变成了非常量移位表达式。因此,如上所述,左操作数,1
被转换为uint64
.
现在,转变正在进行中uint64(1)
,可以使用<<
去尽可能多的地方。您可以将其移至 64 位以上,并且实现将轻松允许它。但在这种情况下,保存着的内存uint64(1)
上面将包含全零。
请注意,根据语言规范,此行为与溢出不同。同样,只要正确的运算符不是常量表达式,语言实现就允许尽可能多的移位。例如,这将起作用:
a := 6666
var j uint64 = 1<<uint64(a) - 1 // non-constant shift expression
这样想吧。早些时候,1
没有打字。它具有任意精度(取决于实现)并且返回整个数字(所有位)。现在,既然它是一个uint64
,仅考虑前 64 位。
这仍然会导致溢出,因为左操作数1
是无类型的,可以包含大量位,返回的值对于uint64
:
var j uint64 = 1<<uint64(66) - 1 // overflow. Note that uint64(64)
fmt.Println(j) // is typed, but it's still a constant