当前位置: 首页 > 面试题库 >

Go语言中的nil切片vs非nil切片vs空切片

冀弘厚
2023-03-14
问题内容

我是Go编程的新手。我已经阅读了Go编程书籍,其中的内容由三部分组成:指向数组的指针,长度和容量。

我对nil slices(切片没有指向len的下层数组,len = 0,cap = 0),非lens切片(其中只有len = 0,cap =
0)和空切片之间感到困惑。

谁能告诉我nil和空片是否相同?如果两者都不相同,那么请说出两者之间的区别是什么?

如何测试切片是否为空?

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


问题答案:

可观察的行为

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

  • 你可以将它们传递到内置len()cap()功能
  • 您可以for range在它们之上(将为0次迭代)
  • 您可以对其进行切片(不违反Spec:Slice表达式中概述的限制;因此结果也将为空切片)
  • 由于它们的长度为0,因此无法更改其内容(附加值将创建新的切片值)

请参见以下简单示例(一个nil切片和2个非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切片。)

您只能通过将slice值与预先声明的标识符进行比较来区分差异nil,它们在其他各个方面的表现相同。

判断一个切片是空的,简单地比较其长度0len(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

具有非nil片与容量和长度等于0LenCap场肯定会0,但Data指针可能不是。这
不会是,这就是从区别它nil切片。它将指向大小为零的基础数组。

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

如果结构或数组类型不包含大小大于零的字段(或元素),则其大小为零。 两个不同的零大小变量在内存中可能具有相同的地址。

让我们检查一下。为此,我们调用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}

我们看到了什么?

  • 所有片(片头)具有不同的内存地址
  • 所述nil切片具有0数据指针
  • s2s3slice具有相同的数据指针,共享/指向相同的0大小的内存值


 类似资料:
  • 我是一个编程新手。我在go编程书中读到过,片由三部分组成:指向数组的指针、长度和容量。 我感到困惑的零切片(切片没有底层数组指向,len=0,cap=0),非零切片只有len=0,cap=0和空切片。 谁能告诉我零和空切片是否是一回事?如果它们都不同,那么请告诉这两者之间的区别是什么? 如何测试一个切片是否为空? 另外,指针在长度和容量为零的非nil片中保留什么值?

  • 我读过一些关于零片和空片的文章。我相信我对他们之间的差异有一些基本的了解。 我的理解总结:是nil片和返回;而是空片和 然而,这个特殊的例子仍然困扰着我。 请查看下面的链接以获取代码。我的问题是最后两个案例。https://play.golang.org/p/udyHoOlSeP 假设我想比较两个片段,重命名类型和接口匹配以及所有。接收机可以是,即使未定义为按值复制;虽然参数是按值复制的,但只要参

  • 主要内容:从数组或切片生成新的切片,直接声明新的切片,使用 make() 函数构造切片切片(slice)是对数组的一个连续片段的引用,所以切片是一个引用类型(因此更类似于 C/ C++ 中的数组类型,或者 Python 中的 list 类型),这个片段可以是整个数组,也可以是由起始和终止索引标识的一些项的子集,需要注意的是,终止索引标识的项不包括在切片内。 Go语言中切片的内部结构包含地址、大小和容量,切片一般用于快速地操作一块数据集合,如果将数据集合比作切糕的话,切片就是你要的“

  • Go 语言切片是对数组的抽象。 Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。 定义切片 你可以声明一个未指定大小的数组来定义切片: var identifier []type 切片不需要说明长度。 或使用make()函数来创建切片: var s

  • 问题内容: 我按照示例https://tour.golang.org/moretypes/10 进行了修改,希望得到相同的结果。我没有。这是错误还是文档错误?巡回演出 无切片的长度和容量为0。 我的y变量的长度和容量为0。 这是我的输出。 我期待第二个“零”〜我为什么不明白呢? 问题答案: 您参考的文档指出 零个切片的长度和容量为0, 但并非 每个 长度和容量为零的切片都是零个切片。规范仅说 未初

  • 本位主要介绍一下 Go 语言中可变长度的"数组"——切片(slice)。数组有数组的用处,但是其不可变长度的特性,注定了在大多场景下不是很受欢迎。在大多数场景下我们都会选择更加灵活的切片。 1. 切片的创建 切片的声明方式和数组类似,写法上看就是声明一个没有长度的数组:var 切片名 []切片类型。其中切片类型可以是切片本身,也就是切片的切片,就构成了多维的切片。 切片在使用之前必须要初始化,它没