脚本之家,脚本语言编程技术及教程分享平台!
分类导航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服务器之家 - 脚本之家 - Golang - Go 语言常见踩坑记

Go 语言常见踩坑记

2021-10-28 21:40NoSayGrape Golang

For循环在我们日常编码中可能用的很多。在很多业务场景中我们都需要用for循环处理。但golang中的for循环在使用上需要注意一些问题,大家可否遇到。

Go 语言常见踩坑记

引言

本系列会列举一些在Go面试中常见的问题。

切片循环问题

For循环在我们日常编码中可能用的很多。在很多业务场景中我们都需要用for循环处理。但golang中的for循环在使用上需要注意一些问题,大家可否遇到。先看下边这一段代码:

  1. functestSlice(){
  2. a:=[]int64{1,2,3}
  3. for_,v:=rangea{
  4. gofunc(){
  5. fmt.Println(v)
  6. }()
  7. }
  8. time.Sleep(time.Second)
  9. }
  10. output:333

那么为什么会输出的是这个结果呢?

在golang的for循环中,循环内部创建的函数变量都是共享同一块内存地址,for循环总是使用同一块内存去接收循环中的的value变量的值。不管循环多少次,value的内存地址都是相同的。我们可以测试一下:

  1. functestSliceWithAddress(){
  2. a:=[]int64{1,2,3}
  3. for_,v:=rangea{
  4. gofunc(){
  5. fmt.Println(&v)
  6. }()
  7. }
  8. time.Sleep(time.Second)
  9. }
  10. output:
  11. 0xc0000b2008
  12. 0xc0000b2008
  13. 0xc0000b2008

符合预期。如果大家比较感兴趣的话可以去将这段代码的汇编打印出来,就可以发现循环的v一直在操作同一块内存。

同样的,在slice循环这块我们还会遇见另一个有趣的地方,大家可以看看下边这段代码输出什么?

  1. functestRange3(){
  2. a:=[]int64{1,2,3}
  3. for_,v:=rangea{
  4. a=append(a,v)
  5. }
  6. fmt.Println(a)
  7. }

这段代码的输出结果是:[1 2 3 1 2 3],为什么呢?因为golang在循环前会先拷贝一个a,然后对新拷贝的a进行操作,所以循环的次数不会随着append而增多。

interface和nil比较

比如返回了一个空指针,但并不是一个空interface

  1. functestInterface(){
  2. doit:=func(argint)interface{}{
  3. varresult*struct{}=nil
  4. ifarg>0{
  5. result=&struct{}{}
  6. }
  7. returnresult
  8. }
  9. ifres:=doit(-1);res!=nil{
  10. fmt.Println("result:",res)
  11. }
  12. }

输出结果为:result: ,为什么呢?因为在go里边变量有类型和值两个属性,在比较的时候也会比较类型和值都相同才会认为相等。代码中result的类型是指针,值是nil,所以会有这样的输出。

可变参数是空接口类型

当参数的可变参数是空接口类型时,传入空接口的切片时需要注意参数展开的问题。

  1. functestVolatile(){
  2. vara=[]interface{}{1,2,3}
  3. fmt.Println(a)
  4. fmt.Println(a...)
  5. }

输出结果为:

  1. [123]
  2. 123

map遍历时顺序不固定

不要相信map的顺序!

  1. functestMap(){
  2. m:=map[string]string{
  3. "a":"a",
  4. "b":"b",
  5. "c":"c",
  6. }
  7. fork,v:=rangem{
  8. println(k,v)
  9. }
  10. }

具体原因大家可以看一下源码:map.go:mapiterinit,就会发现下边这个代码用来决定从哪开始遍历map。另一个原因是map 在某些特定情况下(例如扩容),会发生key的搬迁重组。而遍历的过程,就是按顺序遍历bucket,同时按顺序遍历bucket中的key。搬迁后,key的位置发生了重大的变化,所以遍历map的结果就不可能按原来的顺序了。

  1. funcmapiterinit(t*maptype,h*hmap,it*hiter){
  2. ......
  3. //decidewheretostart
  4. r:=uintptr(fastrand())
  5. ......
  6. }

原文链接:https://mp.weixin.qq.com/s/tcBH86xcN_VXIq4mzaVRkQ

延伸 · 阅读

精彩推荐
  • GolangGo语言range关键字循环时的坑

    Go语言range关键字循环时的坑

    今天小编就为大家分享一篇关于Go语言range关键字循环时的坑,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来...

    benben_20154202020-05-23
  • Golang深入浅析Go中三个点(...)用法

    深入浅析Go中三个点(...)用法

    这篇文章主要介绍了深入浅析Go中三个点(...)用法,需要的朋友可以参考下...

    踏雪无痕SS6472021-11-17
  • GolangGolang实现四种负载均衡的算法(随机,轮询等)

    Golang实现四种负载均衡的算法(随机,轮询等)

    本文介绍了示例介绍了Golang 负载均衡的四种实现,主要包括了随机,轮询,加权轮询负载,一致性hash,感兴趣的小伙伴们可以参考一下...

    Gundy_8442021-08-09
  • GolangGO语言字符串处理Strings包的函数使用示例讲解

    GO语言字符串处理Strings包的函数使用示例讲解

    这篇文章主要为大家介绍了GO语言字符串处理Strings包的函数使用示例讲解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加...

    Jeff的技术栈6882022-04-14
  • GolangGo语言基础单元测试与性能测试示例详解

    Go语言基础单元测试与性能测试示例详解

    这篇文章主要为大家介绍了Go语言基础单元测试与性能测试示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助祝大家多多进步...

    枫少文7812021-12-05
  • GolangGolang 语言极简类型转换库cast的使用详解

    Golang 语言极简类型转换库cast的使用详解

    本文我们通过 cast.ToString() 函数的使用,简单介绍了cast 的使用方法,除此之外,它还支持很多其他类型,在这没有多多介绍,对Golang 类型转换库 cast相关知...

    Golang语言开发栈6112021-12-02
  • Golanggo语言获取系统盘符的方法

    go语言获取系统盘符的方法

    这篇文章主要介绍了go语言获取系统盘符的方法,涉及Go语言调用winapi获取系统硬件信息的技巧,具有一定参考借鉴价值,需要的朋友可以参考下 ...

    无尽海3862020-04-24
  • GolangGo语言实现自动填写古诗词实例代码

    Go语言实现自动填写古诗词实例代码

    这篇文章主要给大家介绍了关于Go语言实现自动填写古诗词的相关资料,这是最近在项目中遇到的一个需求,文中通过示例代码介绍的非常详细,需要的朋...

    FengY5862020-05-14