说明:
要搞明白range其实很简单,除了简单使用方式外,只需要搞明白两个问题就OK了
第一:range会复制对象、所以得明白range后面操作的对象是谁,
第二:range通过操作符 := 创建的对象是怎么回事,是一次创建还是每次循环都创建新的
弄明白这两点,就真正弄明白了range方法的所有表现
第一点:range的对象,
首先range后的操作对象,是一个数据拷贝,而不是在原始(或者代码上文)类型上操作的。那么拷贝的是什么呢?这要根据具体数据类型也看,range的对象可以分为两种数据类型,一个是值类型(例如:array 、string),一个是引用类型 (slice、map、channel)。值类型就是值拷贝,引用类型就是引用拷贝。这里边还有一个指针,指针的表现跟引用类型一致。
看个例子吧:
package main
import (
"fmt"
)
func main() {
// 值类型应用range方法
var array = [5]int{1, 2, 3, 4, 5}
var array_new [5]int
fmt.Println("原始数组的值:", array)
for index, value := range array {
if index == 0 { // 当遍历第一个元素时修改原始数组中的值
array[1] = 10 * value // 此array跟range后边的array已经不是一个值了
array[2] = 20 * value // 此array跟range后边的array已经不是一个值了
}
array_new[index] = value
}
fmt.Println("原始数组改变后的值:", array)
fmt.Println("依托原始数组创建的新数组的值:", array_new) // 新数组是根据值拷贝创建的,会跟原始数组一致
fmt.Println("--------------------------")
// 引用类型应用range方法
var slice_src = []int{1, 2, 3, 4, 5}
var slice_des = make([]int, 5)
fmt.Println("原始切片的值:", slice_src)
for index, value := range slice_src {
if index == 0 { // 当遍历第一个元素时修改原始切片中的值
slice_src[1] = 10 * value // 此slice_src跟range后边的slice_src指向同一块内存,浅拷贝,引用复制
slice_src[2] = 20 * value // 此slice_src跟range后边的slice_src指向同一块内存,浅拷贝,引用复制
slice_src = append(slice_src, 6, 7, 8, 9) // 印证range 后边slice_src也是一个拷贝
}
slice_des[index] = value
}
fmt.Println("原始切片改变后的值:", slice_src)
fmt.Println("依托原始切片创建的新切片的值:", slice_des) // 新切片是根据引用复制创建的,会跟改变后的切片一致
}
响应结果:
原始数组的值: [1 2 3 4 5]
原始数组改变后的值: [1 10 20 4 5]
依托原始数组创建的新数组的值: [1 2 3 4 5]
--------------------------
原始切片的值: [1 2 3 4 5]
原始切片改变后的值: [1 10 20 4 5 6 7 8 9]
依托原始切片创建的新切片的值: [1 10 20 4 5]
第二点:range通过操作:=创建变量是一次性的
range跟for搭配,是一个循环操作。但是操作符:=前边的变量确不是每次初始化,它只在第一次的时候分配内存,后边不论循环多少次都是修改该内存的值。
看个例子吧:
package main
import (
"fmt"
)
func main() {
var array = [5]int{1, 2, 3, 4, 5}
var cha = make(chan *int, 5)
var chb = make(chan *int, 5)
go func() { // 匿名函数、闭包
for _, v := range array { // 在整个循环过程中,v只声明了一次,后续循环都是更改v的值
cha <- &v
temp := v // 在每次循环中,temp都被重新声明,从新分配内存地址
chb <- &temp
}
close(cha)
close(chb)
}()
for v := range cha {
fmt.Println("cha:", *v) // 都是最后一个值5
}
fmt.Println("--------------------------")
for v := range chb {
fmt.Println("chb:", *v) // 输出1,2,3,4,5
}
}
响应结果:
cha: 5
cha: 5
cha: 5
cha: 5
cha: 5
--------------------------
chb: 1
chb: 2
chb: 3
chb: 4
chb: 5
关于闭包捕获变量的表现请看: