nil切片和空切片的区别
nil切片和空切片底层数组指向的地址一样吗?
不一样,nil切片的底层数组指针也是nil,而长度为0的空切片的底层数组指针是有地址的,指向一个特殊的全局变量 runtime.zerobase
的地址(uintptr类型)。
上图的测试代码:
func main() {
var s1 []int // nil 切片
s2 := make([]int, 0) // 空切片,长度和cap都是0
s3 := make([]int, 0) // 同样是空切片
fmt.Println((*reflect.SliceHeader)(unsafe.Pointer(&s1)))
fmt.Println((*reflect.SliceHeader)(unsafe.Pointer(&s2)))
fmt.Println((*reflect.SliceHeader)(unsafe.Pointer(&s3)))
}
输出:
&{0 0 0}
&{18280512 0 0}
&{18280512 0 0}
从slice源码中,我们可以看到空切片的底层数组指针指向了全局变量 zerobase 的地址:
// ------ runtime/slice.go
func makeslice(et *_type, len, cap int) unsafe.Pointer {
mem, overflow := math.MulUintptr(et.size, uintptr(cap))
if overflow || mem > maxAlloc || len < 0 || len > cap {
if overflow || mem > maxAlloc || len < 0 {
panicmakeslicelen()
}
panicmakeslicecap()
}
// mem是0,故 malloccgc直接返回来 zerobase 的地址
return mallocgc(mem, et, true)
}
// ------ runtime/malloc.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)
}
// ...
}