题目来源: 深信服、知乎、跟谁学
题目解析: GOALNG ROADMAP社区
答案 1:(溪尾)
数组长度是固定的,而切片是可变长的。可以把切片看作是对底层数组的封装,每个切片的底层数据结构中,一定会包含一个数组。数组可以被称为切片的底层数组,切片也可以被看作对数组某一连续片段的引用。因此,Go 中切片属于引用类型,而数组属于值类型,通过内建函数 len,可以取得数组和切片的长度。通过内建函数 cap,可以得到数组和切片的容量。但是数组的长度和容量是相等的,并且都不可变,而且切片容量是有变化规律的。
答案 2:(行飞子)
数组和切片的关系:
切片一旦初始化, 切片始终与保存其元素的基础数组相关联。因此,切片会和与其拥有同一基础数组的其他切片共享存储 ; 相比之下,不同的数组总是代表不同的存储。
数组和切片的区别
切片的长度可能在执行期间发生变化 ,而数组的长度不能变化,可以把切片看成一个长度可变的数组。
数组作为函数参数是进行值传递的,函数内部改变传入的数组元素值不会影响函数外部数组的元素值; 切片作为函数的参数是进行的指针传递,函数内部改变切片的值会影响函数外部的切片元素值。
数组可以比较,切片不能比较(对底层数组的引用)。
答案 3:(栾龙生)
1. Go 切片和 Go 数组
Go 切片,又称动态数组,它实际是基于数组类型做的一层封装。
Go 数组
数组是内置(build-in)类型,是一组同类型数据的集合,它是值类型,通过从 0 开始的下标索引访问元素值。在初始化后长度是固定的,无法修改其长度。当作为方法的参数传入时将复制一份数组而不是引用同一指针。数组的长度也是其类型的一部分,通过内置函数 len(array)获取其长度。
Go 数组与像 C/C++等语言中数组略有不同,如下
- Go 中的数组是值类型,换句话说,如果你将一个数组赋值给另外一个数组,那么,实际上就是将整个数组拷贝一份。因此,在 Go 中如果将数组作为函数的参数传递的话,那效率就肯定没有传递指针高了。
- 数组的长度也是类型的一部分,这就说明
[10]int
和[20]int
不是同一种数据类型。
Go 切片
Go 语言中数组的长度是固定的,且不同长度的数组是不同类型,这样的限制带来不少局限性。
而切片则不同,切片(slice)是一个拥有相同类型元素的可变长序列,可以方便地进行扩容和传递,实际使用时比数组更加灵活,这也正是切片存在的意义。
切片是引用类型,因此在当传递切片时将引用同一指针,修改值将会影响其他的对象。
2. 切片底层
现在就来看一下 Go 语言切片的底层是什么样子吧!
Go 切片(slice)的实现可以在源码包src/runtime/slice.go
中找到。在源码中,slice 的数据结构定义如下。
type slice struct {
array unsafe.Pointer //指向底层数组的指针
len int //切片长度
cap int //切片容量
}
可以看到,组成 Go 切片的三元组分别为指向底层数组的指针,切片长度和切片容量。
指向底层数组的指针
前面已经提到,切片实际是对数组的一层封装。这个指针便是记录其底层数组的地址,也正是切片开始的位置。
切片长度
len
表示切片的长度,即切片中现存有效元素的个数,它不能超过切片的容量。可以通过len()
函数获取切片长度。切片容量
cap
表示切片的容量,即切片能存储元素的多少,通常是从切片的起始元素到底层数组的最后一个元素间的元素个数,当切片容量不足时,便会触发 slice 扩容。可以通过cap()
函数获取切片容量。
下图展示了一个 Go 切片的底层数据结构,这个切片的长度为 3,容量为 6。
3. 切片使用
- 切片定义方式
var a []int //nil切片,和nil相等,一般用来表示一个不存在的切片
var b []int{} //空切片,和nil不相等,一般用来表示一