在Go语言中,零片vs非零片与空片 [英] nil slices vs non-nil slices vs empty slices in Go language

查看:327
本文介绍了在Go语言中,零片vs非零片与空片的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Go编程的新手。我已经读过编程书籍,该片由三件事组成:一个指向数组的指针,长度和容量。



我在零片之间感到困惑(片没有底层数组指向,len = 0,cap = 0),其中只有len = 0,cap = 0和空切片的非零切片



任何人都可以请告诉零和空片是否是相同的东西?
如果两者不同,那么请告诉我们这两者之间有什么区别?



如何测试切片是否为空?



另外,指针在非零切片中的长度和容量为零是什么值?

解决方案

可观察行为



nil 和空片(0容量)不是相同,但他们可观察到的行为是相同的。我的意思是:




  • 你可以将它们传递给内建的 len()
  • 您可以<范围范围 / code>在它们之上(将会是0次迭代)

  • 您可以切片(不违反;所以结果也是一个空片段)

  • 由于它们的长度为0,所以可以不会改变它们的内容(附加一个值会创建一个新的切片值)


    请看这个简单的例子(a nil 切片和2个非 - nil 空切片):

      var s1 [] int // nil slice 
    s2:= [] int {} //非零,空片
    s3:= make([] int,0)/ /非零,空片

    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 [:] ==无)

    为_ =范围s1 {}
    为_ =范围s2 {}
    为_ =范围s3 {}

    输出(尝试在 Go Playground ):

      s1 0 0 true [] true 
    s2 0 0 false [] false
    s3 0 0 false [] false

    (请注意, code> nil slice会产生一个 nil 切片,将非 - > nil slice会产生一个非 - nil 切片。)



    您只能通过比较切片值到预先标识的标识符 nil ,它们在所有其他方面表现相同。



    要判断一个片是否为空,只需比较它的长度为 0 len(s)== 0 。无论它是 nil 切片还是一个非 - nil 切片都没关系,也不要紧它具有积极的能力;

      s:= make([] int,0,100)
    如果它没有元素,它就是空的。 fmt.Println(Empty:,len(s)== 0,但容量:,cap(s))

    打印(在去游乐场上试用):

     空:true,但容量:100 



    引擎盖下



    切片值由 reflect.SliceHeader

      type SliceHeader struct {
    Data uintptr
    Len int
    Cap int
    }

    nil 切片的情况下,该结构将具有其零值,这是它的所有字段将是它们的零值,即: 0



    有一个非 nil 切片的容量和长度等于 0 Len Cap 字段肯定会是 0 ,但数据指针可能不是。它 will 不是,这是将它与 nil 切片区分开来的原因。它会指向一个零大小的底层数组。



    请注意,Go规范允许具有0大小的不同类型的值具有相同的内存地址。 规格:系统考量:尺寸和对齐保证:


    如果结构或数组类型的大小不超过零,则它们的大小为零。 两个不同的零大小变量可能在内存中具有相同的地址。

    让我们来检查一下。为此,我们呼叫 不安全 的帮助。包,并获取我们切片值的 reflect.SliceHeader structview:

      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}

    我们看到了什么?




    • 所有切片(切片标头)具有不同的内存地址

    • nil 切片具有 0 数据指针

    • s2 s3 切片具有相同的数据指针,共享/指向相同的0大小的内存值


    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.

    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.

    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?

    Also, what value does the pointer holds in non-nil slices, whose length and capacity are zero?

    解决方案

    Observable behavior

    nil and empty slices (with 0 capacity) are not the same, but their observable behavior is the same. By this I mean:

    • You can pass them to the builtin len() and cap() 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)

    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 {}
    

    Output (try it on the Go Playground):

    s1 0 0 true [] true
    s2 0 0 false [] false
    s3 0 0 false [] false
    

    (Note that slicing a nil slice results in a nil slice, slicing a non-nil slice results in a non-nil slice.)

    You can only tell the difference by comparing the slice value to the predeclared identifier nil, they behave the same in every other aspect.

    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))
    

    Prints (try it on the Go Playground):

    Empty: true , but capacity: 100
    

    Under the hood

    A slice value is represented by a struct defined in reflect.SliceHeader:

    type SliceHeader struct {
        Data uintptr
        Len  int
        Cap  int
    }
    

    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.

    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.

    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.

    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\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)))
    

    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}
    

    What do we see?

    • All slices (slice headers) have different memory addresses
    • The nil slice has 0 data pointer
    • s2 and s3 slices do have the same data pointer, sharing / pointing to the same 0-sized memory value

    这篇关于在Go语言中,零片vs非零片与空片的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆