golang for循环_通过两道面试题讲解 Go for range 循环原理

本文详细介绍了 Go 语言中 for range 循环在遍历切片时的工作原理,强调了其并非死循环,并通过示例解释了在遍历切片并保存指针到映射时可能出现的问题。文章揭示了 for range 的底层实现,包括对切片、数组、字符串和映射的遍历,并提醒开发者注意值与指针的差异,以避免潜在的编程错误。
摘要由CSDN通过智能技术生成
e02b4948484ff6e34a37bcab997b6a00.png

下面的代码是死循环么?

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 := 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", 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 = 

数组

// Lower a for range over an array.// The loop we generate://   len_temp := len(range)//   range_temp := range//   for index_temp = 0; index_temp 

字符串

// 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 

作者:code

链接:https://segmentfault.com/a/1190000023477520

来源:SegmentFault 思否

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

完整底层代码:https://github.com/golang/gofrontend/blob/e387439bfd24d5e142874b8e68e7039f74c744d7/go/statements.cc#L5384

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值