具有 const 或非常量值的 len() 的不同行为

zan*_*ngw 12 constants bit-shift go

下面的代码

const s = "golang.go"

var a byte = 1 << len(s) / 128
Run Code Online (Sandbox Code Playgroud)

的结果a是4。但是,更改const svar s如下之后

var s = "golang.go"

var a byte = 1 << len(s) / 128
Run Code Online (Sandbox Code Playgroud)

现在的结果a是0。

还有其他测试代码如下

const s = "golang.go"

var a byte = 1 << len(s) / 128     // the result of a is 4
var b byte = 1 << len(s[:]) / 128  // the result of b is 0

var ss = "golang.go"

var aa byte = 1 << len(ss) / 128    // the result of aa is 0
var bb byte = 1 << len(ss[:]) / 128 // the result of bb is 0
Run Code Online (Sandbox Code Playgroud)

奇怪的是,b评估长度时结果为 0s[:]

我尝试根据golang 规范来理解它

如果 s 是字符串常量,则表达式 len(s) 是常量。如果 s 的类型是数组或指向数组的指针,并且表达式 s 不包含通道接收或(非常量)函数调用,则表达式 len(s) 和 cap(s) 是常量

但我失败了。有人可以向我解释得更清楚吗?

icz*_*cza 9

不同之处在于,当s为常量时,表达式被解释为常量表达式并执行,使用无类型整数类型并生成int类型。当s是变量时,表达式将被解释为非常量表达式,并使用byte类型执行。

规格: 运营商:

移位表达式中的右操作数必须具有整数类型或者是可由 type 值表示的无类型常量uint如果非常量移位表达式的左操作数是无类型常量,则它首先会隐式转换为移位表达式仅由其左操作数替换时所假定的类型。

s当是变量时,引用的部分适用。该表达式是非常量移位表达式 ( 1 << len(s)),因为s是变量(因此len(s)是非常量),并且左操作数是无类型常量 ( 1)。如果移位表达式仅被其左操作数替换,则将so1转换为假定的类型:

var a byte = 1 << len(s) / 128
Run Code Online (Sandbox Code Playgroud)

替换为

var a byte = 1 / 128
Run Code Online (Sandbox Code Playgroud)

在此变量声明byte中将使用类型,因为该类型用于变量a。所以回到原来的:byte(1)左移9将是0,除以128将也是0

s是常量时,int将被使用,因为Spec: Constant 表达式:

如果常量移位表达式的左操作数是无类型常量,则结果是整型常量;否则它是与左操作数相同类型的常量,必须是整数类型。

这里1will 不会被转换为byte,但是1 << len(s)=> 1 << 9will be 512,除以128will be 4


Vol*_*ker 7

Go 中的 Constant 的行为与您想象的不同。它们是“任意精度和_un_typed”。

表达式是一个常量const consts = "golang.go"表达式1 << len(consts) / 128,并作为具有任意精度的常量表达式进行计算,从而产生一个无类型整数4,该整数可以分配给一个字节,从而产生a == 4

表达式不再是常量表达式,而是必须被计算为某种var vars = "golang.go"类型int。https://go.dev/ref/spec#Operators中如何定义1 << len(vars) / 128

移位表达式中的右操作数必须具有整数类型或者是可由 uint 类型值表示的无类型常量。如果非常量移位表达式的左操作数是无类型常量,则它首先会隐式转换为移位表达式仅由其左操作数替换时所假定的类型。

第二句话适用于你的问题。被1转换为“它将[读取]假设的类型”。拼写出来byte(1) << len(vars)就是 0。

https://go.dev/blog/constants