前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >go语言中的切片有哪些坑

go语言中的切片有哪些坑

作者头像
崔认知
发布2023-09-05 17:59:51
2740
发布2023-09-05 17:59:51
举报
文章被收录于专栏:nobodynobody

简介


go语言中,切片的底层是动态数组,相对长度固定的数组,使用非常广泛,犹如java界的java.util.ArrayList(都是非线程安全),但是切片在使用过程中有几个地方需要我们开发者注意。

坑1:使用函数append增加元素不要忽略返回的新切片


目前为止,go语言中除闭包函数是以引用的方式访问外部变量,其它赋值和函数传参都是传值方式处理的。不过由于切片赋值和传参的底层结构为:

代码语言:javascript
复制
type SliceHeader struct {
  Data uintptr
  Len  int
  Cap  int
}

/usr/local/go/src/reflect/value.go:2760

不管切片元素多少,切片传参复制的结构很轻量,主要在于array底层是一个任意类型指针。当使用函数append添加元素时,底层数组会动态扩容,array指针会指向新的内存地址,如果我们丢弃了函数append返回的新切片,新增加的元素就不会被我们感知了。

示例:

运行结果:

切片使用函数append添加元素,导致底层数组指向新的内存区域,所以函数append返回的新切片也必须替换原切片:

运行结果:

坑2:切割操作共享底层数组导致内存泄露的风险


其实共享底层数组和java7之前String的substring实现的原理一样,只要共享底层数组都会发生内存泄露的风险。

来源:https://bugs.java.com/bugdatabase/view_bug?bug_id=4513622

示例:

运行结果:

切割生成的新切片改变元素之后,原切片也被改变,这是底层数组共享的原因,如果无意中仅使用一个很小的切片保留对非常大的不再有用的切片的引用,GC不回收大切片所占内存,就会导致内存泄露。

java7版本之后,为了解决内存泄露风险,使用数组复制解决:System.arraycopy

go切片的切割操作为了避免内存泄露,目前需要我们自己复制数据:copy函数

运行结果:

坑3:for range 循环中得到的变量也是值拷贝,改变此变量不会改变原切片


示例:

运行结果:

代码语言:javascript
复制
[1 2 3 66 88]
[1 2 3 66 88]

附:切片底层数据结构解析


上面介绍到了切片的底层数据结构为SliceHeader,我们可以使用C语言指针的方式访问切片的底层数据结构:

输出结果:

代码语言:javascript
复制
切片内容:[1 2 3]
长度:3
容量:10
C指针访问底层数组元素第一个:1
C指针访问底层数组元素第二个:2
C指针访问底层数组元素第三个:3

我们通过任意类型指针unsafe.Pointer这个桥梁,把指向切片的指针强制转换为*reflect.SliceHeader指针,这样我们就可以访问切片底层数据结构SliceHeader中的Len(切片长度)、Cap(切片容量)、Data(切片数组的指针)属性。

对于Data(切片数组的指针),我们也是通过任意类型指针unsafe.Pointer这个桥梁,以方便我们利用用C语言指针的运算规则访问底层数组的元素:

代码语言:javascript
复制
fmt.Println("C指针访问底层数组元素第一个:", *((*int)(unsafe.Pointer(p.Data))))
fmt.Println("C指针访问底层数组元素第二个:", *((*int)(unsafe.Pointer(p.Data + unsafe.Sizeof(p.Data)))))
fmt.Println("C指针访问底层数组元素第三个:", *((*int)(unsafe.Pointer(p.Data + 2*unsafe.Sizeof(p.Data)))))

小结


切片的底层数据结构让切片的传值非常轻量、高效,不像数组需要赋值所有数组元素,但是也需要我们考虑以下问题:

坑1:使用函数append增加元素不要忽略返回的新切片

坑2:切割操作共享底层数组导致内存泄露的风险

坑3:for range 循环中得到的变量也是值拷贝,改变此变量不会改变原切片

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-08-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 认知科技技术团队 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档