Golang中Range循环的意外变更问题解析
Golang中Range循环的意外变更问题解析 大家好:
我在为LeetCode上的一个问题(这不重要)编写代码时遇到了一个问题。我写了一个函数,但它没有按我预期的方式工作。我将代码移到了playground。
以下是我的问题:
我打印了每一次循环,item [1 1 2]这一行附近的输出让我感到困惑。
在追加之前,变量result的值是[[] [1] [1 1] [2] [1 2] [1 1 2] [2 2] [1 2 2] [1 1 2 2]],这符合我的预期。
但是在将变量n(其值为3)追加到一个元素中而没有进行赋值操作之后,值变成了[[] [1] [1 1] [2] [1 2] [1 1 2] [2 2] [1 2 2] [1 1 2 3]]。
我不知道为什么会发生这种情况,以及我该如何解决它?
更多关于Golang中Range循环的意外变更问题解析的实战教程也可以访问 https://www.itying.com/category-94-b0.html
好的,我明白了。
前几次循环绕过了这个问题,因为分配了一个新的切片。我现在明白了!
非常感谢!你救了我的命!
更多关于Golang中Range循环的意外变更问题解析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
切片不是不可变的。当你从一个类似 []int{} 的切片开始时,它的容量为零。第一次追加操作会分配一个容量为1的新切片,并将追加的值存储到这个切片中。第二次追加会创建一个容量翻倍(从1到2)的新切片,并将追加的值存储进去。当你第三次向切片追加时,会再次分配一个容量翻倍(到4)的新切片。第四次追加时,第三次追加得到的切片仍有可用容量,因此会复用该切片而无需分配新的。
func main() {
fmt.Println("hello world")
}
这是一个典型的切片引用问题。在Go语言的range循环中,循环变量是复用的,每次迭代时会将当前元素的值复制到同一个变量中。当你操作切片中的切片时,如果直接使用循环变量,可能会导致意外的修改。
以下是问题重现和解决方案:
package main
import "fmt"
func main() {
nums := []int{1, 1, 2, 2}
result := [][]int{{}}
for _, n := range nums {
fmt.Printf("Processing n=%d\n", n)
length := len(result)
for i := 0; i < length; i++ {
// 问题代码:直接使用result[i]会导致引用问题
// item := result[i]
// item = append(item, n)
// result = append(result, item)
// 正确做法:创建新的切片副本
newItem := make([]int, len(result[i]))
copy(newItem, result[i])
newItem = append(newItem, n)
result = append(result, newItem)
fmt.Printf(" result[%d] + %d = %v\n", i, n, newItem)
}
}
fmt.Printf("Final result: %v\n", result)
}
或者更简洁的写法:
package main
import "fmt"
func main() {
nums := []int{1, 1, 2, 2}
result := [][]int{{}}
for _, n := range nums {
length := len(result)
for i := 0; i < length; i++ {
// 使用append创建新切片
newItem := append([]int{}, result[i]...)
newItem = append(newItem, n)
result = append(result, newItem)
}
}
fmt.Printf("Final result: %v\n", result)
}
问题的根本原因是:在Go中,切片是对底层数组的引用。当你执行item := result[i]时,item和result[i]共享同一个底层数组。后续对item的修改可能会影响result中已存在的元素,特别是当append操作触发重新分配时。
在Playground中运行修正后的代码可以看到正确的输出。

