字节笔记本

2026年2月22日

Golang 中 range 的使用方法及注意事项

range 是 Go 语言中非常强大的迭代器,可以遍历数组、切片、字符串、map 等多种数据结构。本文将详细介绍 range 的基本用法以及使用过程中需要注意的关键点。

一、基本用法

range 类似迭代器,可以遍历数组、字符串、map 等等,对象的不同,返回的结果也不同。

go
package main

import "fmt"

func main() {
    // 数组的遍历
    a := [3]int{1, 2, 3}
    for i, n := range a {
        fmt.Println(i, n)
    }

    // 切片的遍历
    b := []int{2, 3, 4}
    for i, n := range b {
        fmt.Println(i, n)
    }

    // map 的遍历
    c := map[string]int{"Hello": 1, "World": 2}
    for k, v := range c {
        fmt.Println(k, v)
    }
}

输出结果:

text
0 1
1 2
2 3
0 2
1 3
2 4
Hello 1
World 2

二、注意事项

1. range 会复制对象,而不是直接在原对象上操作

示例一:

go
package main

import "fmt"

func main() {
    a := [3]int{1, 2, 3}
    for _, v := range a {
        // 复制一份 a 遍历 [1, 2, 3]
        v += 100 // v 是复制对象中的值,不会改变 a 数组元素的值
    }
    fmt.Println(a) // 输出: [1 2 3]
}

示例二:

go
package main

import "fmt"

func main() {
    a := [3]int{1, 2, 3}
    for i, v := range a {
        // i, v 从 a 复制的对象里提取出
        if i == 0 {
            a[1], a[2] = 200, 300
            fmt.Println(a) // 输出 [1 200 300]
        }
        a[i] = v + 100 // v 是复制对象里的元素 [1, 2, 3]
    }
    fmt.Println(a) // 输出 [101, 102, 103]
}

2. 使用 range 迭代遍历引用类型时,底层的数据不会被复制

go
package main

import "fmt"

func main() {
    a := []int{1, 2, 3} // 改成 slice
    for i, v := range a {
        if i == 0 {
            a[1], a[2] = 200, 300
            fmt.Println(a) // [1 200 300]
        }
        a[i] = v + 100
    }
    fmt.Println(a)
}

输出结果:

text
[1 200 300]
[101 300 400]

因为切片的内部结构为 struct slice{*point, len, cap}

数据部分是一个指针,指向地址,复制对象的时候只是把指针的值复制了,而不是重新拷贝一块新的内存再把值放进去,所以修改的时候还是修改的原来的值,和 C++ 里的浅拷贝一样。

总结

理解 range 的复制机制对于编写正确的 Go 代码至关重要:

  • 数组:range 会复制整个数组,遍历时修改的是副本
  • 切片:range 只复制切片头(指针、长度、容量),底层数组共享
  • map:range 返回的是键值对的副本

在实际开发中,如果需要修改原数据结构,建议使用索引直接访问,或者使用指针类型。


原文作者:马谦的博客 文章来源:Go语言中文网

分享: