go - slice 和映射之间的行为差​​异

标签 go

相关问题在这里https://stackoverflow.com/a/12965872/6421681 .

在 go 中,您可以:

func numsInFactorial(n int) (nums []int) {
    // `nums := make([]int)` is not needed
    for i := 1; i <= n; i++ {
        nums = append(nums, i)
    }
    return
}

但是,以下内容不起作用:

func mapWithOneKeyAndValue(k int, v int) (m map[int]int) {
    m[k] = v
    return
}

抛出一个错误:

panic :分配给 nil 映射中的条目

相反,您必须:

func mapWithOneKeyAndValue(k int, v int) map[int]int {
    m := make(map[int]int)
    m[k] = v
    return
}

我找不到此行为的文档。 我已经通读了 effective go 的所有内容,那里也没有提到它。
我知道命名的返回值已定义(即内存已分配;接近于 new 所做的)但未初始化(因此 make 行为未被复制)。
经过一些实验,我相信这种行为可以简化为理解以下代码的行为:

func main() {
    var s []int // len and cap are both 0
    var m map[int]int

    fmt.Println(s) // works... prints an empty slice
    fmt.Println(m) // works... prints an empty map

    s = append(s, 10) // returns a new slice, so underlying array gets allocated
    fmt.Println(s) // works... prints [10]

    m[10] = 10 // program crashes, with "assignment to entry in nil map"
    fmt.Println(m)
}

问题似乎是 append 可能会调用 make 并分配一个新的 slice ,检测到 s 的容量为 0。但是,map 永远不会进行显式初始化。
这个 SO 问题的原因有两个。首先,我想记录下 SO 上的行为。其次,为什么该语言允许 slicemap 的非初始化定义?根据我到目前为止的经验,它似乎是一种实用的语言(即未使用的变量会导致编译失败,gofmt 强制正确格式化),因此阻止代码编译是有意义的。

最佳答案

尝试按索引分配 nil slice - 你会得到“ panic :运行时错误:索引超出范围”(例如:https://play.golang.org/p/-XHh1jNyn5g)

append 函数与 nil 一起工作的唯一原因是 append 函数可以对给定的 slice 进行重新分配。 例如,如果您尝试将第 6 个元素附加到当前容量为 5 的 5 个元素的 slice ,它将创建具有新容量的新数组,从旧数组复制所有信息,并交换给定 slice 中的数据数组指针。在我的理解中,它只是动态数组的golang实现。

因此,nil slice 只是容量不足的 slice 的特例,因此它会在任何追加操作时重新分配。

有关 https://blog.golang.org/go-slices-usage-and-internals 的更多详细信息

关于go - slice 和映射之间的行为差​​异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52681793/

相关文章:

go - tcp 服务器中的空闲超时

go - 包的类型不能用作 vendored 包的类型

Golang 在创建新文件时替换以前打开的文件

go - 验证结构变量是否为空

java - 将无符号字节流转换为有符号字节流 Golang

go - 使用 go 的 HTTP PUT 请求处理程序

go - 在导入包时出现此错误知道如何解决这个问题吗?

go - 用cgo编译出错

go - 如何获取文件所在包的目录,而不是当前工作目录

json - 在 Go 中解码动态 json 内容