英文:
Different behavior of len() with const or non-const value
问题
以下是翻译的内容:
下面是代码:
const s = "golang.go"
var a byte = 1 << len(s) / 128
a
的结果是 4。然而,将 const s
改为以下的 var s
后:
var s = "golang.go"
var a byte = 1 << len(s) / 128
a
的结果现在是 0。
还有其他的测试代码如下:
const s = "golang.go"
var a byte = 1 << len(s) / 128 // a 的结果是 4
var b byte = 1 << len(s[:]) / 128 // b 的结果是 0
var ss = "golang.go"
var aa byte = 1 << len(ss) / 128 // aa 的结果是 0
var bb byte = 1 << len(ss[:]) / 128 // bb 的结果是 0
奇怪的是,通过评估 s[:]
的长度,b
的结果是 0。
我尝试根据 golang 规范 来理解它:
> 如果 s 是一个字符串常量,表达式 len(s) 是常量。如果 s 的类型是数组或指向数组的指针,并且表达式 s 不包含通道接收或(非常量)函数调用,则表达式 len(s) 和 cap(s) 都是常量。
但是我失败了。有人能更清楚地解释一下吗?
英文:
The code below
const s = "golang.go"
var a byte = 1 << len(s) / 128
The result of a
is 4. However, after changing const s
to var s
as following
var s = "golang.go"
var a byte = 1 << len(s) / 128
The result of a
is 0 now.
Also other test codes as below
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
It is weird that b
is 0 with evaluating the length of s[:]
I try to understand it per golang spec
> The expression len(s) is constant if s is a string constant. The expressions len(s) and cap(s) are constants if the type of s is an array or pointer to an array and the expression s does not contain channel receives or (non-constant) function calls
But I failed. Could someone explain it more clearly to me?
答案1
得分: 9
区别在于当s
是常量时,表达式被解释和执行为常量表达式,使用无类型整数类型,并得到int
类型的结果。当s
是变量时,表达式被解释和执行为非常量表达式,使用byte
类型。
引用部分适用于s
是变量的情况。表达式是非常量移位表达式(1 << len(s)
),因为s
是变量(所以len(s)
是非常量),左操作数是无类型常量(1
)。因此,1
会被转换为它在移位表达式中替换为左操作数时所假设的类型:
var a byte = 1 << len(s) / 128
替换为
var a byte = 1 / 128
在这个变量声明中,将使用byte
类型,因为该类型用于变量a
。所以回到原来的问题:byte(1)
左移9
位将得到0
,将其除以128
也将得到0
。
当s
是常量时,将使用int
类型,因为规范:常量表达式
如果常量移位表达式的左操作数是无类型常量,则结果是一个整数常量;否则,它是与左操作数相同类型的常量,左操作数必须是整数类型。
在这里,1
不会被转换为byte
,而是1 << len(s)
=> 1 << 9
将得到512
,除以128
将得到4
。
英文:
The difference is that when s
is constant, the expression is interpreted and executed as a constant expression, using untyped integer type and resulting in int
type. When s
is a variable, the expression is interpreted and executed as a non-constant expression, using byte
type.
> The right operand in a shift expression must have integer type or be an untyped constant representable by a value of type uint
. If the left operand of a non-constant shift expression is an untyped constant, it is first implicitly converted to the type it would assume if the shift expression were replaced by its left operand alone.
The quoted part applies when s
is a variable. The expression is a non-constant shift expression (1 << len(s)
) because s
is a variable (so len(s)
is non-constant), and the left operand is an untyped constant (1
). So 1
is converted to a type it would assume if the shift expression were replaced by its left operand alone:
var a byte = 1 << len(s) / 128
replaced to
var a byte = 1 / 128
In this variable declaration byte
type will be used because that type is used for the variable a
. So back to the original: byte(1)
shifted left by 9
will be 0
, dividing it by 128
will also be 0
.
And when s
is constant, int
will be used because Spec: Constant expressions:
> If the left operand of a constant shift expression is an untyped constant, the result is an integer constant; otherwise it is a constant of the same type as the left operand, which must be of integer type.
Here 1
will not be converted to byte
but 1 << len(s)
=> 1 << 9
will be 512
, divided by 128
will be 4
.
答案2
得分: 7
常量在Go语言中的行为与你可能期望的不同。它们是“任意精度和非类型化”的。
使用const consts = "golang.go"
,表达式1 << len(consts) / 128
是一个常量表达式,并且以任意精度计算为一个非类型化整数4
,可以赋值给一个字节,结果为a == 4
。
使用var vars = "golang.go"
,表达式1 << len(vars) / 128
不再是一个常量表达式,而必须作为某个有类型的整数进行计算。具体定义可参考https://go.dev/ref/spec#Operators
移位表达式的右操作数必须具有整数类型,或者是一个可以表示为uint类型值的非类型化常量。如果非常量移位表达式的左操作数是一个非类型化常量,它首先会被隐式转换为它在移位表达式中将要扮演的类型。
第二句适用于你的问题。1
被转换为“它将要扮演的类型”。具体来说是byte(1) << len(vars)
,结果为0。
英文:
Constant in Go behave differently than you might expect. They are "arbitrary precision and _un_typed".
With const consts = "golang.go"
the expression 1 << len(consts) / 128
is a constant expression and evaluated as a constant expression with arbitrary precision resulting in an untyped integer 4
which can be assigned to a byte resulting in a == 4
.
With var vars = "golang.go"
the expression 1 << len(vars) / 128
no longer is a constant expression but has to be evaluated as some typed int. How is defined in https://go.dev/ref/spec#Operators
> The right operand in a shift expression must have integer type or be an untyped constant representable by a value of type uint. If the left operand of a non-constant shift expression is an untyped constant, it is first implicitly converted to the type it would assume if the shift expression were replaced by its left operand alone.
The second sentence applies to your problem. The 1
is converted to "the type it would [read will] assume". Spelled out this is byte(1) << len(vars)
which is 0.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论