我是一个会编程的新手。我在go programming一书中读到过,切片由三部分组成:指向数组的指针、长度和容量。
我混淆了nil切片(切片没有指向的底层数组,len = 0,cap=0),只有len = 0,cap =0的非nil切片和空切片。
有没有人能说清楚nil和空片是不是一样的东西?如果它们是不同的,那么请告诉我这两者之间的区别是什么?
如何测试切片是否为空?
另外,在长度和容量都为零的非nil切片中,指针持有什么值?
发布于 2017-06-01 19:09:14
可观察的行为
nil和空片(容量为0)并不相同,但它们的可观察行为是相同的。我的意思是:
请看这个简单的示例(一个nil切片和两个非nil空切片):
var s1 []int // nil slice
s2 := []int{} // non-nil, empty slice
s3 := make([]int, 0) // non-nil, empty slice
fmt.Println("s1", len(s1), cap(s1), s1 == nil, s1[:], s1[:] == nil)
fmt.Println("s2", len(s2), cap(s2), s2 == nil, s2[:], s2[:] == nil)
fmt.Println("s3", len(s3), cap(s3), s3 == nil, s3[:], s3[:] == nil)
for range s1 {}
for range s2 {}
for range s3 {}输出(在Go Playground上试用):
s1 0 0 true [] true
s2 0 0 false [] false
s3 0 0 false [] false(请注意,对nil切片进行切片会产生nil切片,对非nil切片进行切片会产生非nil切片。)
您只能通过比较切片值和预声明的标识符nil来区分它们,它们在所有其他方面的行为都是相同的。
要判断切片是否为空,只需将其长度与0:len(s) == 0进行比较。无论它是nil切片还是非nil切片,它的容量是否为正也无关紧要;如果它没有元素,它就是空的。
s := make([]int, 0, 100)
fmt.Println("Empty:", len(s) == 0, ", but capacity:", cap(s))打印(在Go Playground上试用):
Empty: true , but capacity: 100在引擎盖下
切片值由reflect.SliceHeader中定义的结构表示
type SliceHeader struct {
Data uintptr
Len int
Cap int
}在nil切片的情况下,这个结构将有它的零值,即它的所有字段都将是它们的零值,即:0。
具有容量和长度都等于0的非nil片时,Len和Cap字段肯定为0,但Data指针可能不是。它不会是,这就是它与nil切片的区别。它将指向一个大小为零的底层数组。
请注意,Go规范允许大小为0的不同类型的值具有相同的内存地址。Spec: System considerations: Size and alignment guarantees:
如果结构或数组类型不包含大小大于零的字段(或元素),则该类型的大小为零。两个不同的零大小变量在内存中可能具有相同的地址。
让我们来看看这个。为此,我们调用unsafe包的帮助,并“获得”我们的切片值的reflect.SliceHeader结构“视图”:
var s1 []int
s2 := []int{}
s3 := make([]int, 0)
fmt.Printf("s1 (addr: %p): %+8v\n",
&s1, *(*reflect.SliceHeader)(unsafe.Pointer(&s1)))
fmt.Printf("s2 (addr: %p): %+8v\n",
&s2, *(*reflect.SliceHeader)(unsafe.Pointer(&s2)))
fmt.Printf("s3 (addr: %p): %+8v\n",
&s3, *(*reflect.SliceHeader)(unsafe.Pointer(&s3)))输出(在Go Playground上试用):
s1 (addr: 0x1040a130): {Data: 0 Len: 0 Cap: 0}
s2 (addr: 0x1040a140): {Data: 1535812 Len: 0 Cap: 0}
s3 (addr: 0x1040a150): {Data: 1535812 Len: 0 Cap: 0}我们看到了什么?
发布于 2017-06-01 19:09:34
从字面上看,一个切片可以是零:
var n []int
n == nil // true这是唯一简单的情况。“空”片的概念没有很好地定义:带有len(s) == 0的片s肯定是空的,而不管它的容量如何。最明智的做法是:忽略底层实现,永远不需要知道片在内部是如何表示的。重要的是切片的定义行为。
如何测试切片是否为空?
“slice s is empty”最合理的定义是不包含任何元素的slice,也就是len(s) == 0。该定义适用于nil以及非nil切片。
谁能告诉我nil和空片是不是一样的东西?如果它们是不同的,那么请告诉我这两者之间的区别是什么?
从技术上讲,nil片和非nil片是不同的(一个是== nil,另一个是!= nil),但这种区别通常无关紧要,因为您可以对nil片执行append操作,并且len和cap on nil片返回0。
var n []int
len(n) == cap(n) == 0 // true
n = append(n, 123)
len(n) == 1 // true请阅读Go中有关零值的内容以获取更多信息。nil切片类似于nil通道或nil映射:它是未初始化的。您可以通过对它们执行make或通过文字进行初始化。如上所述,没有理由考虑底层表示。
还有,在长度和容量都为零的非nil切片中,指针持有什么值?
这是一个实现细节,可能会因编译器而异,甚至因版本而异。没有人需要知道这一点,才能编写正确和可移植的Go程序。
发布于 2020-10-23 05:58:23
var s1 []int // nil slice
s2 := []int{} // non-nil, empty slice
s3 := make([]int, 0) // non-nil, empty slice警告,如果处理JSON,nil切片将编码为null而不是[],这可能会破坏一些试图在不可迭代的null上迭代(不进行null检查)的(javascript)客户端。
https://stackoverflow.com/questions/44305170
复制相似问题