上面的代码是死循环么?

func main() {    v := []int{1, 2, 3}    for i := range v {        v = append(v, i)    }}

下面的代码先初始化了一个内容为1、2、3的slice,而后遍历这个slice,而后给这个切片追加元素。随着遍历的进行,数组v也在逐步增大,那么这个for循环是一个死循环么?

答案是否。只会遍历三次,v的后果是[0, 1, 2]。并不是死循环,起因就在于for range实现的时候用到了语法糖。

语法糖

语法糖(Syntactic
sugar),也译为糖衣语法,是由英国计算机科学家彼得·蘭丁创造的一个术语,指计算机语言中增加的某种语法,这种语法对语言的性能没有影响,然而更不便程序员应用。
语法糖让程序更加简洁,有更高的可读性。

对于切片的for range,它的底层代码就是:

//   for_temp := range//   len_temp := len(for_temp)//   for index_temp = 0; index_temp < len_temp; index_temp++ {//           value_temp = for_temp[index_temp]//           index = index_temp//           value = value_temp//           original body//   }

能够看到,在遍历之前就获取的切片的长度len_temp := len(for_temp),遍历的次数不会随着切片的变动而变动,下面的代码天然不会是死循环了。

上面的代码有什么问题么?

slice := []int{0, 1, 2, 3}myMap := make(map[int]*int)for index, value := range slice {        fmt.Println(&index, &value)    myMap[index] = &value}fmt.Println("=====new map=====")for k, v := range myMap {    fmt.Printf("%d => %d\n", k, *v)}

这也是理论编码中有可能会遇到的问题,循环切片,index和value地址一开始调配好后,前面还是那个地址,把切片值的地址保留到myMap中,这样的操作后果是:

__

== ===new map=====0 => 31 => 32 => 33 => 3

后果齐全一样,都是最初一次遍历的值。通过下面的底层代码看下,遍历后的值赋给了value,而在咱们的例子中,会把value的地址保留到myMap的值中。这里的value是个「全局变量」,所以赋完值之后myMap外面所有的值都是value,所以构造都是一样的而且是最初一个值。

留神,这里必须是保留指针才会有问题,如果间接保留的是value,因为 Golang 是值拷贝,所以值会从新复制再保留,这种状况下后果就会是正确的了。

切片For Range原理

总结一下,通过For Range遍历切片, 首先,计算遍历次数(切片长度);每次遍历,都会把以后遍历到的值寄存到一个全局变量index中。

其它语法糖

另外,For Range 不光反对切片。其它的语法糖底层代码。

map

// Lower a for range over a map.// The loop we generate://   var hiter map_iteration_struct//   for mapiterinit(type, range, &hiter); hiter.key != nil; mapiternext(&hiter) {//           index_temp = *hiter.key//           value_temp = *hiter.val//           index = index_temp//           value = value_temp//           original body//   }

channel

// Lower a for range over a channel.// The loop we generate://   for {//           index_temp, ok_temp = <-range//           if !ok_temp {//                   break//           }//           index = index_temp//           original body//   }

数组

// Lower a for range over an array.// The loop we generate://   len_temp := len(range)//   range_temp := range//   for index_temp = 0; index_temp < len_temp; index_temp++ {//           value_temp = range_temp[index_temp]//           index = index_temp//           value = value_temp//           original body//   }

字符串

// Lower a for range over a string.// The loop we generate://   len_temp := len(range)//   var next_index_temp int//   for index_temp = 0; index_temp < len_temp; index_temp = next_index_temp {//           value_temp = rune(range[index_temp])//           if value_temp < utf8.RuneSelf {//                   next_index_temp = index_temp + 1//           } else {//                   value_temp, next_index_temp = decoderune(range, index_temp)//           }//           index = index_temp//           value = value_temp//           // original body//   }

残缺底层代码