内容简介:上面的代码先初始化了一个内容为1、2、3的答案是否。只会遍历三次,语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·蘭丁发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用。 语法糖让程序更加简洁,有更高的可读性。
下面的代码是死循环么?
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 { myMap[index] = &value } fmt.Println("=====new map=====") for k, v := range myMap { fmt.Printf("%d => %d\n", k, *v) }
这也是实际编码中有可能会遇到的问题,循环切片,把切片值的地址保存到 myMap
中,这样的操作结果是:
=====new map===== 0 => 3 1 => 3 2 => 3 3 => 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 // }
推荐阅读: Go Range Loop Internals
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
图片转BASE64编码
在线图片转Base64编码工具
UNIX 时间戳转换
UNIX 时间戳转换