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 是拷贝,改数据或取地址请用下标操作原容器。

笔记加载中…