• Golang 笔试面试学习第二天之range


    Golang 笔试面试学习第二天之range

    Golang轻松学习


    个人博客站点:
    简书: 😽 猫轻王 https://www.jianshu.com/u/6cce817646be
    掘金: 😽 猫轻王 https://juejin.cn/user/1640918680347453
    CSDN: 😽 猫轻王 https://blog.csdn.net/moer0
    个人项目主页:https://github.com/moercat


    一、range 是什么?

    Go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对。

    for 循环的 range 格式可以对 slice、map、数组、字符串等进行迭代循环。

    二、详细代码

    1.range 的逻辑

    代码如下(示例):

    func main() {
    
         slice := []int{0,1,2,3}
         m := make(map[int]*int)
    
         for key,val := range slice {
             m[key] = &val
         }
    
        for k,v := range m {
            fmt.Println(k,"->",*v)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    输出:

    0 -> 3
    1 -> 3
    2 -> 3
    3 -> 3
    
    • 1
    • 2
    • 3
    • 4

    原理:

     for range 循环的时候会创建每个元素的副本,而不是元素的引用,
     所以 m[key] = &val 取的都是变量 val 的地址,
     map 中的所有元素的值都是变量 val 的地址,
     最后 val 被赋值为3,所有输出都是3
    
    • 1
    • 2
    • 3
    • 4

    也就是通过 for range 时,由于此时都是进行地址的传递,直到map结束时,所有的变量取得都是同一个 val 的地址,尽管该地址上的值在range过程中不断被替换成新的元素,但地址却不会更换。直到最终打印时,取得同一个地址的值,自然都是最后一个元素的值。

    2.slice 无限遍历的误解

    代码如下(示例):

    func main() {
    
    	slice := []int{0, 1, 2, 3}
    
    	fmt.Println("before append",slice)
    
    	for _, val := range slice {
    		slice = append(slice, val)
    	}
    
    	fmt.Println("after append",slice)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    输出:

    before append [0 1 2 3]
    after append [0 1 2 3 0 1 2 3]
    
    • 1
    • 2

    由于其他语言可能存在底层实现方式的不同,导致数据无限遍历。通过上面的代码逻辑可以更好地理解第一点中 for range 循环的时候会创建每个元素的副本,而不是元素的引用的含义,也就是其实每一次for range 都是将要遍历的对象赋给一个新的对象,对副本遍历后append 进原来的地址。这自然不会影响当前还在遍历的副本,也就不会导致无限遍历的出现。

    3.range 的逻辑修正

    代码如下(示例):

    func main() {
    
         slice := []int{0,1,2,3}
         m := make(map[int]*int)
    
         for key,val := range slice {
             value := val
             m[key] = &value
         }
    
        for k,v := range m {
            fmt.Println(k,"===>",*v)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    输出:

    0 ===> 0
    1 ===> 1
    2 ===> 2
    3 ===> 3
    
    • 1
    • 2
    • 3
    • 4

    那么理解了range的逻辑,那么我们自然就懂得如何改正错误,上文是因为取得地址一直不变,但是值一直改变,导致最终所有元素都是同一个地址同一个值。那么我们只需要让他每次地址都改变一次就能改正错误。
    value := val
    简单的逻辑就是每次遍历完,生成一个新变量,那么变量自然地址是新的,而赋值之后也是每次遍历的值,不会因为地址的相同而被覆盖。自然 m[key] 每次值都是新变量的地址,也就不会出现错误。


    总结

    for range 时,由于此时都是进行地址的传递,直到map结束时,所有的变量取得都是同一个 val 的地址,尽管该地址上的值在range过程中不断被替换成新的元素,但地址却不会更换。直到最终打印时,取得同一个地址的值,自然都是最后一个元素的值。每次遍历完,生成一个新变量,那么变量自然地址是新的,而赋值之后也是每次遍历的值,不会因为地址的相同而被覆盖。自然 m[key] 每次值都是新变量的地址,也就不会出现错误。


    希望这个博客能对你有所益处。我是轻王,我为自己代言。

  • 相关阅读:
    SpringBoot 场景开发多面手成长手册
    springboot整合MeiliSearch轻量级搜索引擎
    程序员的专属浪漫——用3D Engine 5分钟实现烟花绽放效果
    Windows下hadoop单点部署
    Python zip函数及用法
    黑马瑞吉外卖之过滤器后台登录验证(详细笔记说明)
    [架构之路-243]:目标系统 - 纵向分层 - 架构是表面轮廓、内部骨架、未来蓝图,企业组织架构、信息系统架构、软件架构、应用程序就架构
    Ubuntu:VS Code IDE安装ESP-IDF【保姆级】
    8. 基于消影点进行相机内参(主点)的标定
    Elasticsearch基础操作演示总结
  • 原文地址:https://blog.csdn.net/moer0/article/details/126513817