本文已参与「新人创作礼」活动,一起开启掘金创作之路
零切片、空切片、nil切片
Go语言中的零切片、空切片、nil切片这三者是不同的东西,切片的创建方式有很多种,不同方式创建出来的切片也不一样。
总结:零切片长度和容量不为0,切片内部数组的元素都是零值;nil切片的长度和容量都为0,和nil比较结果为true;空切片的长度和容量为0,但是和nil的比较结果为false
-
零切片
我们把切片内部数组的元素都是零值或者底层数组的内容全是 nil的切片叫做零切片,使用make创建的、长度、容量都不为0的切片就是零值切片:
slice1 := make([]int,5) // 0 0 0 0 0 slice2 := make([]*int,5) // nil nil nil nil nil -
nil切片
nil切片(nil slice)的长度和容量都为0,并且和nil比较的结果为true,采用直接创建切片的方式、new创建切片的方式都可以创建nil切片:
var slice3 []int var slice4 = *new([]int)这种情况可以用于需要返回slice的函数,当函数出现异常的时候,保证函数依然会有nil的返回值。
但是错误的用法,会报数组越界的错误,因为只是声明了slice,却没有给实例化的对象。
slice3[1] = 0 //runtime error: index out of range [1] with length 0 -
空切片
空切片(empty slice)的长度和容量也都为0,但是和nil的比较结果为false,因为所有的空切片的数据指针都指向同一个地址 0xc42003bda0;使用字面量、make可以创建空切片:
var slice5 = []int{} var slice6 = make([]int, 0)空切片指向的 zerobase 内存地址是一个神奇的地址,从 Go 语言的源代码中可以看到它的定义:
// base address for all 0-byte allocations var zerobase uintptr // 分配对象内存 func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { ... if size == 0 { return unsafe.Pointer(&zerobase) } ... }当我们查询或者处理一个空的切片的时候,这非常有用,它会告诉我们返回的是一个切片,但是切片内没有任何值。
测试代码:
func TestDefineSlice(t *testing.T) {
//零切片
slice1 := make([]int,5) // 0 0 0 0 0
slice2 := make([]*int,5) // nil nil nil nil nil
t.Log(slice1, len(slice1),cap(slice1),slice1==nil)
t.Log(slice2,len(slice2),cap(slice2),slice2==nil)
//nil切片
var slice3 []int
var slice4 = *new([]int)
t.Log(slice3, len(slice3),cap(slice3),slice3==nil)
t.Log(slice4, len(slice4),cap(slice4),slice4==nil)
//slice3[1] = 0 //runtime error: index out of range [1] with length 0
//空切片
var slice5 = []int{}
var slice6 = make([]int, 0)
t.Log(slice5, len(slice5),cap(slice5),slice5==nil)
t.Log(slice6, len(slice6),cap(slice6),slice6==nil)
}
运行结果:
=== RUN TestDefineSlice
slice_test.go:79: [0 0 0 0 0] 5 5 false
slice_test.go:80: [<nil> <nil> <nil> <nil> <nil>] 5 5 false
slice_test.go:85: [] 0 0 true
slice_test.go:86: [] 0 0 true
slice_test.go:91: [] 0 0 false
slice_test.go:92: [] 0 0 false
--- PASS: TestDefineSlice (0.00s)
面试题
最后我们再来看一道面试题:
var a interface{}
fmt.Println(a == nil) // true, the interface doesn't point to anything
var someNilSlice []int
fmt.Println(someNilSlice == nil) // true, just some nil slice
a = someNilSlice
fmt.Println(a == nil) // false, now the interface does point to something