前言
Go 中的for range组合可以和方便的实现对一个数组或切片进行遍历,但是在某些情况下使用for range时很可能就会被”坑”,下面用一段代码来模拟下:
func main() {
arr1 := []int{1, 2, 3}
arr2 := make([]*int, len(arr1))
for i, v := range arr1 {
arr2[i] = &v
}
for _, v := range arr2 {
fmt.Println(*v)
}
}
代码解析:
创建一个int slice,变量名为arr1并初始化 1,2,3
在Go语言中,`for range` 是一种非常常用的迭代语法,尤其在遍历数组、切片、map 和通道等集合类型时。然而,正如标题所言,“详解Go语言中for range的坑”,在某些特定场景下,不恰当的使用`for range`可能会导致意外的结果。本文将深入探讨这个问题,并提供解决方案。
让我们看一个简单的例子,该例子展示了使用`for range`遍历切片并创建指针切片时出现的问题:
```go
func main() {
arr1 := []int{1, 2, 3}
arr2 := make([]*int, len(arr1))
for i, v := range arr1 {
arr2[i] = &v
}
for _, v := range arr2 {
fmt.Println(*v)
}
}
```
在这个例子中,我们创建了一个`int`类型的切片`arr1`,并初始化为[1, 2, 3]。然后,我们创建了一个`*int`类型的切片`arr2`,用于存储`arr1`中每个元素的指针。我们使用`for range`遍历`arr1`,并将每个元素的指针赋值给`arr2`。按预期,我们应该看到输出1, 2, 3。但实际情况是,输出是3, 3, 3。为什么会这样呢?
原因在于`for range`在遍历值类型时,`v`变量是一个副本,当我们使用`&`获取指针时,实际上获取的是`v`这个临时变量的指针。由于`v`在每次循环中都被重置,因此`arr2`中存储的都是同一个指针,它最终指向`arr1`最后一个元素的值的副本,即3。
为了解决这个问题,我们可以采取以下几种方法:
1. **传递原始指针**:
修改`for range`循环,直接将`arr1`的元素地址赋值给`arr2`,这样就能确保每个元素的指针是正确的。
```go
for i := range arr1 {
arr2[i] = &arr1[i]
}
```
2. **使用临时变量**:
在每次循环中创建一个新的临时变量`t`,将`v`的值复制给`t`,然后获取`t`的指针。
```go
for i, v := range arr1 {
t := v
arr2[i] = &t
}
```
3. **使用闭包**:
利用闭包来捕获`v`的值,确保每个闭包都有自己的局部变量。
```go
for i, v := range arr1 {
func(v int) {
arr2[i] = &v
}(v)
}
```
Go语言官方文档中也提到了这种常见的错误,提醒开发者注意`for range`在处理指针和引用类型时的行为。理解这种行为对于编写高效、无bug的Go代码至关重要。通过上述的解决方案,我们可以避免`for range`的陷阱,正确地遍历和操作数据结构。