说明:
要搞明白range其实很简单,除了简单使用方式外,只需要搞明白两个问题就OK了
第一:range会复制对象、所以得明白range后面操作的对象是谁,
第二:range通过操作符 := 创建的对象是怎么回事,是一次创建还是每次循环都创建新的
弄明白这两点,就真正弄明白了range方法的所有表现
第一点:range的对象,
首先range后的操作对象,是一个数据拷贝,而不是在原始(或者代码上文)类型上操作的。
那么拷贝的是什么呢?这要根据具体数据类型也看,range的对象可以分为两种数据类型,一个是值类型(例如:array 、string),一个是引用类型 (slice、map、channel)。
值类型就是值拷贝,引用类型就是引用拷贝。
这里边还有一个指针,指针的表现跟引用类型一致。
看个例子吧:
package mainimport ( "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 mainimport ("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 <- &vtemp := 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: 5cha: 5cha: 5cha: 5cha: 5--------------------------chb: 1chb: 2chb: 3chb: 4chb: 5
关于闭包捕获变量的表现请看:Golang匿名函数与闭包