arrays - 如何在 Go 中保持强引用?

标签 arrays pointers go slice

有什么方法可以在 Go 中保留强引用?

给定以下复杂的代码:

package main

import (
  "fmt"
)

func main() {   
  slice := make([]int, 5)
  slice[3] = 25 // whatever index between 0 and 4 included I don't care

  slicesArray := make([]*[]int, 2)  
  slicesArray[0] = &slice

  fmt.Println((*(slicesArray[0]))[3])
  slice = nil
  fmt.Println((*(slicesArray[0]))[3])
}

这个程序当然会崩溃,因为一旦将 slice 设置为 nil,垃圾收集器就会将内存区域标记为脏。

但是有没有办法告诉 Go 我的 slice 指针 slice 应该保持对这些 slice 的强引用? 此外,保留对 slice 的引用而不是将 slicesArray 声明为 [][]int 是否会增加内存使用量?是否有任何文档清楚地说明这是如何工作的?

最佳答案

TL;DR:(摘要)

您只需要复制 slice 值即可。 slice 类型的值是底层数组的描述符:

slice2 := slice

现在 slice2 将引用相同的共享底层数组,因此在 slice 归零后可以访问它。

长:

slice := make([]int, 5)

这将创建一个名为 slice 的局部变量,类型为 []int(并将使用引用大小为 5 的数组的描述符进行初始化> 通过 make 在后台创建)。它是一个 slice ,它是在后台自动创建的底层数组的连续部分的描述符。

slicesArray[0] = &slice

这会将局部变量(名为 slice)的地址存储到 slicesArray 的第 0 个元素中。请注意,slicesArray 只是指针的一部分(这些指针可能指向 []int 类型的值,但现在这并不重要)。

所以仍然没有创建原始 slice 的副本。

因此,当您将 []int 类型的唯一值(即名为 slice 的局部变量)归零时,您将唯一的 slice 值归零(并且您将丢失对其支持数组的唯一引用)。

您想在 slice 归零后保留 slice 值吗?只需复制它( slice 值)。复制 slice 类型的值只会复制描述符,不会复制支持数组;并且副本将引用相同的后备数组(它是共享的):

slice2 := slice // makes a copy of the slice value
slice = nil

在此之后,*(slicesArray[0]) 仍将指向一个值为 nil 的 slice ,但我们有原始 slice 的副本(和共享支持数组)。

这样做:

slicesArray[0] = &slice2
fmt.Println((*(slicesArray[0]))[3])

将再次打印 25Go Playground

您应该保留 slice 值还是 slice 指针?

由于 slice 只是相对较小的描述符,因此您应该保留并使用 slice 值。 slice 值已经包含对后备数组的“引用”。通过使用指向 slice 的指针添加另一个间接只会使事情复杂化并稍微减慢速度。 slice 已经被设计成小巧、高效和灵活的(与 Go 中的“真实”数组相比)。

当然,在某些情况下,指向 slice 值的指针也可能有用,例如,如果您想要创建一个类似于内置 append() 的函数在不返回新 slice 的情况下运行。或者,当您创建一个底层类型为 slice 类型的自定义类型时,您为该类型定义了修改 slice 值的方法(在这种情况下,指针接收器是必需的)。

进一步阅读:(详细解释一切的文档)

Go Slices: usage and internals

Arrays, slices (and strings): The mechanics of 'append'

关于arrays - 如何在 Go 中保持强引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30876241/

相关文章:

google-app-engine - Golang Cloud SDK - gcloud app deploy 找不到导入包

javascript - 如何删除javascript数组元素?

C++数组默认参数,同时使函数重载

c - 未初始化和空指针之间的区别

c - int 指针不会返回,触发 SIGABRT

arrays - Golang 将相同级别的 XML 元素解码到数组中

go - 如何在中间件中获取请求体

php - 在 Laravel 中将数组值插入数据库

c - ASCII 字符未打印出来

c++ - 如何在C++中增加指针的指针值