快速理解Go数组和切片的内部实现原理

RyuGou · · 1476 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

很多人对Go语言的``array``和``slice``傻傻分不清楚,今天我们就从底层出发,来聊聊它俩到底有什么区别。 # 数组 几乎所有计算机语言,数组的实现都是相似的:一段连续的内存,Go语言也一样,Go语言的数组底层实现就是一段连续的内存空间。每个元素有唯一一个索引(或者叫``下标``)来访问。如下图所示,下图是``[5]int{1:10, 2:20}``数组的内部实现逻辑图: ![array.png](https://static.studygolang.com/180812/0633448a1ac48e603d17286bc4a2a277.png) 由于内存连续,CPU很容易计算索引(即数组的``下标``),可以快速迭代数组里的所有元素。 Go语言的数组不同于C语言或者其他语言的数组,C语言的数组变量是指向数组第一个元素的指针;而Go语言的数组是一个值,Go语言中的数组是值类型,一个数组变量就表示着整个数组,意味着Go语言的数组在传递的时候,传递的是原数组的拷贝。你可以理解为Go语言的数组是一种有序的``struct`` # slice 切片是一个很小的对象,是对数组进行了抽象,并提供相关的操作方法。切片有三个属性字段:长度、容量和指向数组的指针。 ![slice_1.png](https://static.studygolang.com/180812/ee57751a9a09093004b557727ef1bcd6.png) 上图中,``ptr``指的是指向array的pointer,``len``是指切片的长度, ``cap``指的是切片的容量。现在,我想你对数组和切片有了一个本质的认识。 ## 切片有多种声明方式,每种初始化方式对应的逻辑图是怎样的呢? ### 对于``s := make([]byte, 5)``和``s := []byte{...}``的方式 ![slice2.png](https://static.studygolang.com/180812/ea0a08a0b903dea44d0c132969985184.png) ### 对于``s = s[2:4]``的方式 ![slice3.png](https://static.studygolang.com/180812/56ffd410a1fc4b032906027f24cc6250.png) ### 对于``nil``的切片即``var s []byte``对应的逻辑图是 ![slice4.png](https://static.studygolang.com/180812/af618778b5903997ee3ab7794c6295a4.png) 在此有一个说明:``nil``切片和``空``切片是不太一样的,空切片即``s := make([]byte, 0) ``或者``s := []byte{}``出来的切片 空切片的逻辑图为: ![slice5.png](https://static.studygolang.com/180812/332389822e57f83adf693474e9adde97.png) 空切片指针不为nil,而nil切片指针为nil。但是,不管是空切片还是nil切片,对其调用内置函数``append()``、``len``和``cap``的效果都是一样的,感受不到任何区别。 ## 扩容 slice这种数据结构便于使用和管理数据集合,可以理解为是一种“动态数组”,``slice``也是围绕动态数组的概念来构建的。既然是动态数组,那么slice是如何扩容的呢? 请记住以下两条规则: + 如果切片的容量小于1024个元素,那么扩容的时候slice的cap就翻番,乘以2;一旦元素个数超过1024个元素,增长因子就变成1.25,即每次增加原来容量的四分之一。 + 如果扩容之后,还没有触及原数组的容量,那么,切片中的指针指向的位置,就还是原数组,如果扩容之后,超过了原数组的容量,那么,Go就会开辟一块新的内存,把原来的值拷贝过来,这种情况丝毫不会影响到原数组。 知道了一下规则,请看下面程序,试问输出结果: ```go import ( "fmt" ) func main(){ array := [4]int{10, 20, 30, 40} slice := array[0:2] newSlice := append(append(append(slice, 50), 100), 150) newSlice[1] += 1 fmt.Println(slice) } ``` 输出: ``` [10 20] ``` 答对了吗? 参考文献: 《Go in action》 https://blog.golang.org/go-slices-usage-and-internals

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1476 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传