Go for range

作者: Sun东辉 | 来源:发表于2022-05-24 07:11 被阅读0次
type student struct {
    Name string
    Age  int
}

func three() int {
    m := make(map[string]*student)
    stus := []student{
        {Name: "zhou", Age: 24},
        {Name: "li", Age: 23},
        {Name: "wang", Age: 22},
    }

    for _, stu := range stus {
        m[stu.Name] = &stu
    }

    for k, v := range m {
        println(k, "=>", v.Name) 
        // zhou => wang 
        // li => wang 
        // wang => wang
    }
}

这里的输出为什么不是?

zhou => zhou 
li => li
wang => wang

这里的原因在于使用 for range 遍历的时候,k, v 使用的同一块内存。

为什么?直接查看源码

func walkRange(nrange *ir.RangeStmt) ir.Node {
    ...
    switch t.Kind() {
            case types.TMAP:
                // order.stmt allocated the iterator for us.
                // we only use a once, so no copy needed.
                ha := a

                hit := nrange.Prealloc
                th := hit.Type()
                // depends on layout of iterator struct.
                // See cmd/compile/internal/reflectdata/reflect.go:MapIterType
                keysym := th.Field(0).Sym // 定义了 key 的地址
                elemsym := th.Field(1).Sym // ditto 定义了 value 的地址

                fn := typecheck.LookupRuntime("mapiterinit")

                fn = typecheck.SubstArgTypes(fn, t.Key(), t.Elem(), th)
                init = append(init, mkcallstmt1(fn, reflectdata.TypePtr(t), ha, typecheck.NodAddr(hit)))
                nfor.Cond = ir.NewBinaryExpr(base.Pos, ir.ONE, ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, keysym), typecheck.NodNil())

                fn = typecheck.LookupRuntime("mapiternext")
                fn = typecheck.SubstArgTypes(fn, th)
                nfor.Post = mkcallstmt1(fn, typecheck.NodAddr(hit))

                key := ir.NewStarExpr(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, keysym))
                if v1 == nil {
                    body = nil
                } else if v2 == nil {
                    body = []ir.Node{ir.NewAssignStmt(base.Pos, v1, key)}
                } else {
                    elem := ir.NewStarExpr(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, elemsym))
                    a := ir.NewAssignListStmt(base.Pos, ir.OAS2, []ir.Node{v1, v2}, []ir.Node{key, elem})
                    body = []ir.Node{a}
                }
                ...
}

由此可以发现,key 和 value 在进入 range 时,地址被重新指定了。

除了遍历时的地址外,还有一点需要注意,map 遍历的顺序是无序的。源码如下

func makemap(t *maptype, hint int, h *hmap) *hmap {
    mem, overflow := math.MulUintptr(uintptr(hint), t.bucket.size)
    if overflow || mem > maxAlloc {
        hint = 0
    }

    // initialize Hmap
    if h == nil {
        h = new(hmap)
    }
    h.hash0 = fastrand() // 随机种子
    ...
}

即 map 在运行时的会使用随机种子,因此,上面程序中得到的 wang 是由随机种子决定的。

相关文章

  • Go Range 内部实现

    原文:Go Range Loop Internals Go 里的 range 循环用起来非常方便,但我总觉得它在不...

  • 八、Go range的用法

    八、Go语言range Go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slic...

  • Go for range

    这里的输出为什么不是? 这里的原因在于使用 for range 遍历的时候,k, v 使用的同一块内存。 为什么?...

  • Learn Golang in Days - Day 11

    Learn Golang in Days - Day 11 要点 Range Go语言中range关键字用于for...

  • 第01天(基本类型、流程控制)_05

    26_for的使用.go 27_range的使用.go 28_break和continue的区别.go 29_go...

  • Go Range内幕

    前言:翻译一篇国外小哥对Range的分析... 原文链接:go-range loop internals 我们都知...

  • Go-for range

    Go中 有关循环,有两种,一种是for i :=0;i < len(x); i++ 的经典模式,另外一种是for ...

  • Go array range

  • Go 学习

    go语言局部变量分配在栈还是堆Golang 垃圾回收剖析go语言坑之for range

  • Go语言For Range小记

    由于Go的SDK及IDE的升级,语法、方法都会有变化,就会出现黄色警告。 为了防止按提示更新后线上出现重大BUG,...

网友评论

    本文标题:Go for range

    本文链接:https://www.haomeiwen.com/subject/jnxdprtx.html