当你写
1<<64
在
1上面没有一个
int64。这是一个 常量常量 。根据语言规格:
常量表达式总是精确地求值;中间值和常量本身可能需要比该语言中任何预声明类型支持的精度大得多的精度。
因此,常量文字会在编译时进行评估,因为它不是语言实现的特定类型,所以它可能非常大。
下面实际上会给出一个溢出错误:
var i int64i = 1<<65 - 1
因为现在常量文字表达式求值的值大于
int64可以包含的值。
在此处阅读有关此内容的更多信息。
要知道您的示例代码为何适用
i = 65,请参考Golang
规范中的以下规范:
移位表达式中的右操作数必须具有无符号整数类型,或者是可以转换为无符号整数类型的无类型常量。如果 非恒定移位表达式 的
左操作数是未类型化的常数,则首先将其转换为假定移位表达式被其左操作数单独替换时所假定的类型 。
上面的膨胀部分与您的代码有关。考虑下面的代码:
a := 66var j uint64 = 1<<uint64(a) - 1
在移位运算符中,右操作数是 一个非恒定的表达式 。因此整个移位操作成为 非恒定移位表达式 。因此,如上所述,将左操作数
1转换为
uint64。
现在,已在上
uint64(1)进行了平移,可以使用所需的任意
<<多个位置进行平移。您可以将其移位到64位以上,并且实现将很容易地允许它。但是在这种情况下,保存
uint64(1)上述内容的内存将包含全零。
请注意,根据语言规范,此行为与溢出并不相同。同样,只要正确的运算符不是常量表达式,语言限制就可以进行任意多的移位。因此,例如,这将起作用:
a := 6666var j uint64 = 1<<uint64(a) - 1 // non-constant shift expression
这样想吧。较早时,未输入
1。它具有任意精度(取决于实现),并且正在返回整数(所有位)。现在,由于它是a
uint64,因此仅考虑了前64位。
这仍然会导致溢出,因为左操作数
1是untypes,并且可能包含大量位,对于a返回的值太大
uint64:
var j uint64 = 1<<uint64(66) - 1 // overflow. Note that uint64(64)fmt.Println(j) // is typed, but it's still a constant



