如何在 golang 中创建具有动态长度而不是切片的数组?

acr*_*ing -1 arrays go slice

例如:我想使用反射将切片的数据作为数组来操作它。

func inject(data []int) {
    sh := (*reflect.SliceHeader)(unsafe.Pointer(&data))
    dh := (*[len(data)]int)(unsafe.Pointer(sh.Data))
    printf("%v\n", dh)
}
Run Code Online (Sandbox Code Playgroud)

此函数将发出编译错误,因为len(data)它不是常量。我该如何解决?

kos*_*tix 5

要添加到@icza的注释中,您可以使用&data[0]—assumingdata是一个已初始化的切片轻松提取底层数组。IOW,这里没有必要跳过这些:第一个切片元素的地址实际上是切片底层数组中第一个插槽的地址——这里没有魔法。

由于获取数组元素的地址会创建对该内存的引用(只要垃圾收集器需要考虑),您可以安全地让切片本身超出范围,而不必担心该数组的内存无法访问。

唯一不能对结果指针真正做的事情是传递取消引用它的结果。这仅仅是因为 Go 中的数组在它们的类型中编码了它们的长度,所以你将无法创建一个函数来接受这样的数组——因为你事先不知道数组的长度。

现在请停下来想一想。

从切片中提取后备数组后,您就有了一个指向数组内存的指针。为了合理地携带它,您还需要携带数组的长度……但这正是切片所做的:它们将支持数组的地址与其中的数据长度(以及容量)打包在一起。

因此,我真的认为您应该重新考虑您的问题,因为从我的立场来看,我倾向于认为这不是问题。

某些情况下,使用指向从切片中提取的后备数组的指针可能会有所帮助:例如,当“池化”此类数组(例如, via sync.Pool)以在某些情况下减少内存流失时,但这些是具体问题。如果你有一个具体的问题,请解释它, 而不是你试图解决的问题——@Flimzy 所说的。


更新我想我应该更好地解释

您无法真正处理结果指针传递取消引用它的结果。

少量。

Go 中数组(与切片相反)的一个关键点是数组——就像 Go 中的一切一样——是按值传递 的,对于数组,这意味着它们的数据被复制。

也就是说,如果你有

var a, b [8 * 1024 * 1024]byte
...
b = a
Run Code Online (Sandbox Code Playgroud)

该语句b = a将真正复制 8 MiB 的数据。这显然同样适用于函数的参数。

Slices 通过持有一个指向底层(后备)数组的指针来回避这个问题。所以切片值是一种struct包含一个指针和两个整数的小类型。因此,复制它确实很便宜,但“作为交换”它具有引用语义:原始值及其副本都指向相同的后备数组——即引用相同的数据。

我真的建议你按照指定的顺序阅读这两篇文章:

  1. https://blog.golang.org/go-slices-usage-and-internals
  2. https://blog.golang.org/slices