Go 语言范围 Range
range 是 Go 的万能遍历关键字,配合 for 使用,可以遍历**数组、切片、字符串、map、通道(channel)**五类结构。range 每次迭代返回"下标(键)+ 值",不需要的一侧用 _ 忽略;只写一个变量时拿到的是下标/键而不是值,这点新手常踩坑。
遍历数组与切片:双值
package main
import "fmt"
func main() {
arr := [3]int{10, 20, 30} // 数组
for i, v := range arr { // 双值:i 是下标,v 是值
fmt.Printf("%d:%d ", i, v)
}
fmt.Println() // 输出:0:10 1:20 2:30
slice := []string{"春", "夏", "秋"}
for _, v := range slice { // _ 忽略下标
fmt.Print(v, " ") // 输出:春 夏 秋
}
}
单值写法与 _ 忽略
s := []int{5, 6, 7}
for i := range s { // 单值拿到的是下标!
fmt.Print(i, " ") // 输出:0 1 2
}
sum := 0
for _, v := range s { // 只要值,忽略下标
sum += v
}
fmt.Println(sum) // 输出:18
遍历字符串:byte 与 rune 的区别
普通下标循环按 byte(字节) 遍历,遇到中文会拆碎;range 则自动按 rune(Unicode 码点) 遍历,每次迭代是一个完整字符,下标是该字符的字节偏移:
str := "Go语言"
for i, c := range str { // c 是 rune
fmt.Printf("%d:%c ", i, c)
}
fmt.Println() // 输出:0:G 1:o 2:语 5:言
for i := 0; i < len(str); i++ { // 按字节遍历
fmt.Printf("%x ", str[i]) // 中文的 3 个字节被拆开
}
想统计"字符个数"应使用 range 或 utf8.RuneCountInString,而不是 len(str)。
遍历 map
m := map[string]int{"语文": 90, "数学": 85}
for k, v := range m { // map 遍历顺序是随机的
fmt.Printf("%s=%d ", k, v) // 数学=85 语文=90(顺序不定)
}
// 只取键:for k := range m
遍历通道
通道必须先 close,range 才能在收完所有值后自动结束,否则会永久阻塞:
ch := make(chan int, 3)
ch <- 1
ch <- 2
close(ch) // 发送完毕就关闭
for v := range ch { // 通道迭代每轮只有单值
fmt.Print(v, " ") // 输出:1 2
}
range 的副本陷阱
range 数组/切片时 v 是元素的拷贝,直接改 v 不影响原数据;且 v 是"循环变量",取 &v 得到的是该变量自身的地址而非元素,别指望用 &v 收集元素地址。range 数组还会先复制整个数组,迭代中改原数组不影响遍历结果:
arr := [3]int{1, 2, 3}
for i, v := range arr {
if i == 0 {
arr[0] = 100 // 改的是原数组
}
fmt.Print(v, " ") // 输出:1 2 3,遍历的是副本
}
// 想修改元素要按下标操作:for i := range arr { arr[i] *= 2 }
小结:range 一站遍历数组/切片/字符串/map/通道;字符串按 rune 出字符、通道要先 close、单值取下标;v 是拷贝,改数据或取地址请用下标操作原容器。