我有一个Go程序,可以对字符串常数的长度执行移位和除运算,但输出并不是我所期望的.下面是代码:

package main

import "fmt"

const s = "123456789" // len(s) == 9

// len(s) is a constant expression,
// whereas len(s[:]) is not.
var a byte = 1 << len(s) / 128
var b byte = 1 << len(s[:]) / 128

func main() {
    fmt.Println(a, b) // outputs: 4 0
}

在该程序中,a和b是使用涉及位移动和除除的类似公式计算的.然而,a和b的输出分别为4和0,这似乎违反直觉,因为这两个操作涉及相同的字符串长度和相似的算术.有人能解释一下为什么a和b会产生不同的结果吗?

  • 在这种情况下,除以128和位转移有什么作用?
  • 为什么len(s[:])被认为不是一个常数表达,这如何影响判断?

如果能深入了解这些表达如何以不同的方式判断以及为什么它们会在Go中导致不同的输出,我将不胜感激.

推荐答案

差异在于len(s)是一个常数,而len(s[:])不是一个常数,导致第一个位移是一个常数位移,第二个位移是一个非常数位移.

第一个例子是一个常数位移操作,将在"const"空间中执行,结果将转换为byte(因为它适合一个字节).

第二个例子是非恒定位移运算,因此根据规范,首先将1转换为byte,然后将位移和除作为byte值进行(位移结果不适合byte,所以结果将是0),除以128将再次成为0.

相关部分从Spec: Operators:

位移表达中的右操作数必须为integer type(Go 1.13),或者是可由类型uint的值表示的非类型化常数.如果非常数位移式的左操作数是未类型化的常数,则首先将其隐式转换为如果位移式仅由其左操作数替换时所假设的类型.

查看相关:Golang shift operator conversion

至于为什么len(s[:])不是一个常数,请参阅Spec: Slice expressions:

untyped strings外,如果切片操作数是字符串或切片,则切片操作的结果是与操作数相同类型的非常数值.For untyped string operands the result is a non-constant value of type 101.

您可能会争论,如果索引值也是常数,那么对常数字符串进行切片可能会产生常数值,但这种专门化目前不可用,而且由于它会改变当前程序的结果(例如您的问题中),因此将来可能不会这样做.

Go相关问答推荐

使用恶意软件 scanner (ClamAV)时的Google云存储桶上传文件验证

Gorm foreign 密钥

Go GORM创建表,但不创建列

一种基于时间的Golang函数节制器

仅呈现一个模板的GIN Web应用程序

创建服务时云运行触发器执行失败

Makefilego version和read命令

一次打印用户输入的字符串n次

迭代字符串并用映射值替换原始字符串中的值的惯用方法

go中跨域自定义验证的问题

从带有嵌套括号的字符串中提取值

在 Go 中将元数据从一个 JPEG 复制到另一个

如何使用名称具有包名称的嵌套 struct 启动 go struct

读取非UTF8编码的文件内容并正确打印出来

如何仅提取时间作为持续时间

为什么在单独的 go routine 中需要 wg.Wait() 和 close() ?

如何 Select 前 N 个元素 Gin-Gorm

Golang 使用 docker 将敏感数据作为参数传递

不能使用 *T 类型的变量作为参数类型

如果在调用 http.Get(url) 时发生错误,我们是否需要关闭响应对象?