Go 语言中的 nil 切片 vs 非 nil 切片 vs 空切片 [英] nil slices vs non-nil slices vs empty slices in Go language
问题描述
我是 Go 编程的新手.我在 Go 编程书中读到切片由三件事组成:指向数组的指针、长度和容量.
I am a newbee to Go programming. I have read in go programming book that slice consists of three things: a pointer to an array, length and capacity.
我在 nil 切片(切片没有可指向的底层数组,len = 0,cap=0)、只有 len = 0,cap = 0 的非 nil 切片和空切片之间感到困惑.
I am getting confused between nil slices(slice has no underlying array to point to, len = 0,cap=0), non-nil slices where only len = 0,cap = 0 and empty slices.
谁能告诉我 nil 和 empty slice 是不是一回事?如果两者都不同,那么请说说这两者的区别是什么?
Can anyone please tell whether nil and empty slices are same things? If they both are different, then please tell what is the difference between those two?
如何判断一个切片是否为空?
How to test whether a slice is empty or not?
另外,指针在长度和容量为零的非 nil 切片中持有什么值?
Also, what value does the pointer holds in non-nil slices, whose length and capacity are zero?
推荐答案
可观察行为
nil
和空切片(容量为 0)不一样,但它们的可观察行为是一样的.我的意思是:
Observable behavior
nil
and empty slices (with 0 capacity) are not the same, but their observable behavior is the same. By this I mean:
- 您可以将它们传递给内置的
len()
和cap()
函数 - 您可以
范围
超过它们(将是 0 次迭代) - 您可以对它们进行切片(通过不违反 规范:切片表达式 中概述的限制);因此结果也将是一个空切片)
- 因为它们的长度是 0,所以你不能改变它们的内容(附加一个值会创建一个新的切片值)
- You can pass them to the builtin
len()
andcap()
functions - You can
for range
over them (will be 0 iterations) - You can slice them (by not violating the restrictions outlined at Spec: Slice expressions; so the result will also be an empty slice)
- Since their length is 0, you can't change their content (appending a value creates a new slice value)
看这个简单的例子(一个 nil
切片和 2 个非 nil
空切片):
See this simple example (a nil
slice and 2 non-nil
empty slices):
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 上试试):
Output (try it on the Go Playground):
s1 0 0 true [] true
s2 0 0 false [] false
s3 0 0 false [] false
(请注意,切片 nil
切片会导致 nil
切片,切片非 nil
切片会导致非 nil
切片.)
(Note that slicing a nil
slice results in a nil
slice, slicing a non-nil
slice results in a non-nil
slice.)
您只能通过将切片值与预先声明的标识符 nil
进行比较来区分它们,它们在其他方面的表现都相同.
You can only tell the difference by comparing the slice value to the predeclared identifier nil
, they behave the same in every other aspect.
要判断切片是否为空,只需将其长度与 0
进行比较:len(s) == 0
.它是nil
切片还是非nil
切片都没有关系,它是否具有正容量也没有关系;如果没有元素,则为空.
To tell if a slice is empty, simply compare its length to 0
: len(s) == 0
. It doesn't matter if it's the nil
slice or a non-nil
slice, it also doesn't matter if it has a positive capacity; if it has no elements, it's empty.
s := make([]int, 0, 100)
fmt.Println("Empty:", len(s) == 0, ", but capacity:", cap(s))
打印(在 Go Playground 上试试):
Prints (try it on the Go Playground):
Empty: true , but capacity: 100
引擎盖下
切片值由reflect.SliceHeader
:
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
在 nil
切片的情况下,这个结构体将有它的零值,即它的所有字段都将是它们的零值,即:0
.
In case of a nil
slice, this struct will have its zero value which is all its fields will be their zero value, that is: 0
.
有一个非nil
切片,容量和长度都等于0
、Len
和Cap
字段肯定会是 0
,但 Data
指针可能不是.它不会,这就是它与 nil
切片的区别.它将指向一个零大小的底层数组.
Having a non-nil
slice with both capacity and length equal to 0
, Len
and Cap
fields will most certainly be 0
, but the Data
pointer may not be. It will not be, that is what differentiates it from the nil
slice. It will point to a zero-sized underlying array.
请注意,Go 规范允许大小为 0 的不同类型的值具有相同的内存地址.规范:系统注意事项:大小和对齐保证:
Note that the Go spec allows for values of different types having 0 size to have the same memory address. Spec: System considerations: Size and alignment guarantees:
如果结构或数组类型不包含大小大于零的字段(或元素),则其大小为零.两个不同的零大小变量可能在内存中具有相同的地址.
A struct or array type has size zero if it contains no fields (or elements, respectively) that have a size greater than zero. Two distinct zero-size variables may have the same address in memory.
让我们检查一下.为此,我们调用 unsafe
包的帮助,并获得"我们切片值的 reflect.SliceHeader
结构视图":
Let's check this. For this we call the help of the unsafe
package, and "obtain" the reflect.SliceHeader
struct "view" of our slice values:
var s1 []int
s2 := []int{}
s3 := make([]int, 0)
fmt.Printf("s1 (addr: %p): %+8v
",
&s1, *(*reflect.SliceHeader)(unsafe.Pointer(&s1)))
fmt.Printf("s2 (addr: %p): %+8v
",
&s2, *(*reflect.SliceHeader)(unsafe.Pointer(&s2)))
fmt.Printf("s3 (addr: %p): %+8v
",
&s3, *(*reflect.SliceHeader)(unsafe.Pointer(&s3)))
输出(在 Go Playground 上试试):
Output (try it on the 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}
我们看到了什么?
- 所有切片(切片头)具有不同的内存地址
nil
切片有0
数据指针s2
和s3
切片确实具有相同的数据指针,共享/指向相同的 0 大小的内存值
- All slices (slice headers) have different memory addresses
- The
nil
slice has0
data pointer s2
ands3
slices do have the same data pointer, sharing / pointing to the same 0-sized memory value
这篇关于Go 语言中的 nil 切片 vs 非 nil 切片 vs 空切片的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!