golang中的空切片和nil切片

发布时间 2023-07-07 15:32:03作者: 易先讯

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序列化时也不一样,涉及到的时候要注意。