len()函数在const或非const值上的行为不同

huangapple go评论77阅读模式
英文:

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 = &quot;golang.go&quot;

var a byte = 1 &lt;&lt; len(s) / 128

The result of a is 4. However, after changing const s to var s as following

var s = &quot;golang.go&quot;

var a byte = 1 &lt;&lt; len(s) / 128

The result of a is 0 now.

Also other test codes as below

const s = &quot;golang.go&quot;

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

var ss = &quot;golang.go&quot;

var aa byte = 1 &lt;&lt; len(ss) / 128    // the result of aa is 0
var bb byte = 1 &lt;&lt; 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.

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 quoted part applies when s is a variable. The expression is a non-constant shift expression (1 &lt;&lt; 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 &lt;&lt; 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 &lt;&lt; len(s) => 1 &lt;&lt; 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。

https://go.dev/blog/constants

英文:

Constant in Go behave differently than you might expect. They are "arbitrary precision and _un_typed".

With const consts = &quot;golang.go&quot; the expression 1 &lt;&lt; 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 = &quot;golang.go&quot; the expression 1 &lt;&lt; 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) &lt;&lt; len(vars) which is 0.

https://go.dev/blog/constants

huangapple
  • 本文由 发表于 2021年11月29日 17:27:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/70152343.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定