加入收藏 | 设为首页 | 会员中心 | 我要投稿 厦门网 (https://www.xiamenwang.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程 > 正文

Go 语言经常踩坑记

发布时间:2021-11-03 18:09:34 所属栏目:编程 来源:互联网
导读:引言 本系列会列举一些在Go面试中常见的问题。 切片循环问题 For循环在我们日常编码中可能用的很多。在很多业务场景中我们都需要用for循环处理。但golang中的for循环在使用上需要注意一些问题,大家可否遇到。先看下边这一段代码: func testSlice() { a :=
引言 本系列会列举一些在Go面试中常见的问题。   切片循环问题 For循环在我们日常编码中可能用的很多。在很多业务场景中我们都需要用for循环处理。但golang中的for循环在使用上需要注意一些问题,大家可否遇到。先看下边这一段代码:   func testSlice() {      a := []int64{1,2,3}      for _, v := range a {          go func() {              fmt.Println(v)          }()      }            time.Sleep(time.Second)  }    output: 3 3 3  那么为什么会输出的是这个结果呢?   在golang的for循环中,循环内部创建的函数变量都是共享同一块内存地址,for循环总是使用同一块内存去接收循环中的的value变量的值。不管循环多少次,value的内存地址都是相同的。我们可以测试一下:   func testSliceWithAddress() {      a := []int64{1,2,3}      for _, v := range a {          go func() {              fmt.Println(&v)          }()        }        time.Sleep(time.Second)  }    output:          0xc0000b2008          0xc0000b2008          0xc0000b2008  符合预期。如果大家比较感兴趣的话可以去将这段代码的汇编打印出来,就可以发现循环的v一直在操作同一块内存。   同样的,在slice循环这块我们还会遇见另一个有趣的地方,大家可以看看下边这段代码输出什么?   func testRange3() {      a := []int64{1,2,3}      for _, v := range a {          a = append(a, v)      }        fmt.Println(a)  }  这段代码的输出结果是:[1 2 3 1 2 3],为什么呢?因为golang在循环前会先拷贝一个a,然后对新拷贝的a进行操作,所以循环的次数不会随着append而增多。   interface和nil比较 比如返回了一个空指针,但并不是一个空interface   func testInterface() {      doit := func(arg int) interface{} {          var result * struct{} = nil          if arg > 0 {              result = &struct{}{}          }            return result      }        if res := doit(-1); res != nil {          fmt.Println("result:", res)      }  }  输出结果为:result: ,为什么呢?因为在go里边变量有类型和值两个属性,在比较的时候也会比较类型和值都相同才会认为相等。代码中result的类型是指针,值是nil,所以会有这样的输出。   可变参数是空接口类型 当参数的可变参数是空接口类型时,传入空接口的切片时需要注意参数展开的问题。   func testVolatile() {      var a = []interface{}{1, 2, 3}        fmt.Println(a)      fmt.Println(a...)  }  输出结果为:   [1 2 3]  1 2 3  map遍历时顺序不固定   不要相信map的顺序!   func testMap() {      m := map[string]string{          "a": "a",          "b": "b",          "c": "c",      }        for k, v := range m {          println(k, v)      }  }  具体原因大家可以看一下源码:map.go:mapiterinit,就会发现下边这个代码用来决定从哪开始遍历map。另一个原因是map 在某些特定情况下(例如扩容),会发生key的搬迁重组。而遍历的过程,就是按顺序遍历bucket,同时按顺序遍历bucket中的key。搬迁后,key的位置发生了重大的变化,所以遍历map的结果就不可能按原来的顺序了。   func mapiterinit(t *maptype, h *hmap, it *hiter) {          ......      // decide where to start      r := uintptr(fastrand())          ......  } 

(编辑:厦门网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读