切片和映射之间的行为差​​异

相关问题在这里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: assignment to entry in nil map


相反,您必须:


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并分配一个新的切片来检测容量s是0. 但是,map永远不会得到显式初始化。

这个 SO 问题的原因有两个。首先,我想记录下 SO 上的行为。slice其次,为什么该语言允许和的非初始化定义map?根据我到目前为止的经验,它似乎是一种实用的语言(即未使用的变量导致编译失败,gofmt 强制正确格式化),因此阻止代码编译是有意义的。


holdtom
浏览 118回答 2
2回答

繁华开满天机

尝试按索引分配 nil 切片 - 你会得到“恐慌:运行时错误:索引超出范围”append 函数与 nil 一起工作的唯一原因是 append 函数可以对给定的切片进行重新分配。例如,如果您尝试将第 6 个元素附加到当前容量为 5 的 5 个元素的切片,它将创建具有新容量的新数组,从旧数组复制所有信息,并交换给定切片中的数据数组指针。在我的理解中,它只是动态数组的 golang 实现。因此,nil slice 只是容量不足的 slice 的特例,因此它会在任何追加操作时重新分配。

慕斯王

一个 nil 映射在读取时表现得像一个空映射,但尝试写入一个 nil 映射会导致运行时恐慌;不要那样做。要初始化地图,请使用内置的 make 函数似乎 nil 映射被认为是有效的空映射,这就是他们不为其自动分配内存的原因。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go