golang中的空切片和nil切片
Golang中的切片是很基本的数据结构,它的底层是一个数组,表层是一个包含三个变量的结构体:
type slice struct {
array unsafe.Pointer //指针,指向底层的数组
length int //切片的长度
capcity int //切片的容量
}
当把一个切片赋值给另一个切片时,实际上是对切片表层结构体的浅拷贝。
在编写项目代码时遇到了切片的特殊状态,这里对其做一个比较总结:
l 零切片
[零切片]其实并不是一种特殊的切片,它表示切片底层数组的二进制内容都是0,如下的s就是一个零切片:
var s = make([]int, 10)
fmt.Println(s)
------------
[0 0 0 0 0 0 0 0 0 0]
l 空切片和nil切片
这两种特殊形态的切片容易混淆。
首先,我们看看长度为0的切片可以怎样创建:
var s1 []int
var s2 = []int{}
var s3 = make([]int, 0)
var s4 = *new([]int) // new 函数返回是指针类型,所以需要使用 * 号来解引用
fmt.Println(len(s1), len(s2), len(s3), len(s4))
fmt.Println(cap(s1), cap(s2), cap(s3), cap(s4)) //切片容量
fmt.Println(s1, s2, s3, s4)
----------------
0 0 0 0
0 0 0 0
[] [] [] []
可以看到,这四种形式的输出结果一模一样。但实际上,s1和s4是[nil切片],s2和s3是[空切片],它们是有区别的。
研究这四种形式的内部结构,可以使用go语言中的高级内容——unsafe.Pointer来转换任意变量类型。
因为切片的内部结构是一个包含三个变量的结构体,第一个变量是一个指向内存地址的指针变量。我们可以将这个结构体看成长度为3的整形数组[3]int,用unsafe.Pointer来转换:
var a1 = *(*[3]int)(unsafe.Pointer(&s1))
var a2 = *(*[3]int)(unsafe.Pointer(&s2))
var a3 = *(*[3]int)(unsafe.Pointer(&s3))
var a4 = *(*[3]int)(unsafe.Pointer(&s4))
fmt.Println(a1)
fmt.Println(a2)
fmt.Println(a3)
fmt.Println(a4)
---------------------
[0 0 0]
[824634199592 0 0]
[824634199592 0 0]
[0 0 0]
输出为[0 0 0]的就是[nil切片],而824634199592是一个特殊的内存地址,所有类型的[空切片]都共享这一个内存地址。
l 空切片和nil切片在使用上的注意事项
var s1 []int
var s2 = []int{}
fmt.Println(s1 == nil)
fmt.Println(s2 == nil)
-------
true
false
在写代码时,最好不要创建[空切片],统一使用[nil切片]。在将切片和nil进行比较来执行某些逻辑时要特别小心。
另外,这两种切片在JSON序列化时也不一样,涉及到的时候要注意。
本文来自博客园,作者:易先讯,转载请注明原文链接:https://www.cnblogs.com/gongxianjin/p/17535118.html