Go 如何将切片转换为数组?

Go 如何将切片转换为数组?,go,Go,我正在尝试编写一个读取RPM文件的应用程序。每个块的开头都有一个魔法字符[4]字节 这是我的结构 type Lead struct { Magic [4]byte Major, Minor byte Type uint16 Arch uint16 Name string OS uint16 SigType uint16 } 我正在努力做到以下几点: lead := Lead{

我正在尝试编写一个读取RPM文件的应用程序。每个块的开头都有一个魔法字符
[4]字节

这是我的结构

type Lead struct {
  Magic        [4]byte
  Major, Minor byte
  Type         uint16
  Arch         uint16
  Name         string
  OS           uint16
  SigType      uint16
}
我正在努力做到以下几点:

lead := Lead{}
lead.Magic = buffer[0:4]

我在网上搜索,不知道如何从一个切片到一个数组(不复制)。我总是可以制作神奇的
[]字节
(甚至
uint64
),但我更好奇的是,如果需要,我将如何从类型
[]字节
转换为
[4]字节

您在该结构中分配了四个字节,并希望为该四字节部分分配一个值。没有复制就没有概念上的方法可以做到这一点

查看内置的
副本
了解如何执行此操作。

尝试以下操作:

copy(lead.Magic[:], buf[0:4])
内置的copy方法只会将一个切片复制到一个切片,而不会将一个切片复制到一个数组

您必须欺骗copy,使其认为数组是一个切片

copy(varLead.Magic[:], someSlice[0:4])
或者使用for循环进行复制:

for index, b := range someSlice {

    varLead.Magic[index] = b

}
或者像祖帕那样使用文字。我已添加到他们的工作示例中


不要。切片本身就足以满足所有用途。go lang中的数组应被视为slice的底层结构。在每种情况下,仅使用切片。你不必排列你自己。您只需使用切片语法即可完成所有操作。阵列仅用于计算机。在大多数情况下,切片更好,代码更清晰。即使在其他情况下,切片仍然足以反映您的想法。

您可以通过一次读取完成整个过程,而不是单独读取每个字段。如果字段长度固定,则可以执行以下操作:

lead := Lead{}

// make a reader to dispense bytes so you don't have to keep track of where you are in buffer
reader := bytes.NewReader(buffer)

// read into each field in Lead, so Magic becomes buffer[0:4],
// Major becomes buffer[5], Minor is buffer[6], and so on...
binary.Read(reader, binary.LittleEndian, &lead)

在不复制的情况下,您可以使用下一个Go 1.17(2021年第3季度)将切片转换为数组指针

这称为“取消切片”,再次为您返回指向的指针,无需任何复制/分配:

请参见,现在使用和实施

将切片转换为数组指针将生成指向切片的底层数组的指针。
如果切片的长度小于数组的长度, 出现运行时恐慌

s:=make([]字节,2,4)
s0:=(*[0]字节)(s)//s0!=无
s2:=(*[2]字节)(s)/&;s2[0]==&;s[0]
s4:=(*[4]字节)(s)//panics:len([4]字节)>len(s)
var t[]字符串
t0:=(*[0]字符串)(t)//t0==nil
t1:=(*[1]字符串)(t)//panics:len([1]字符串)>len(s)
因此,在您的情况下,提供的
Magic
类型是
*[4]字节

lead.Magic = (*[4]byte)(buffer)
注意:类型别名也会起作用:

类型A[4]int
var s=(*A)([]int{1,2,3,4})

为什么要转换为数组指针?如中所述:

这样做的一个动机是使用数组指针允许编译器在编译时对常量索引进行范围检查

这样的函数:

func foo(a[]int)int
{
返回a[0]+a[1]+a[2]+a[3];
}
可以转化为:

func foo(a[]int)int
{
b:=(*[4]int)(a)
返回b[0]+b[1]+b[2]+b[3];
}
允许编译器只检查一次所有边界,并给出有关超出范围索引的编译时错误

:

一个很好使用的例子是使树节点或链表节点的类尽可能小,以便您可以将尽可能多的类塞进一级缓存线。
每个节点都有一个指向左子节点的指针,右子节点由指向左子节点+1的指针访问。
这将为右节点指针保存8个字节

要做到这一点,您必须预先分配向量或数组中的所有节点,以便它们按顺序排列在内存中,但当您需要性能时,这是值得的。
(这还有一个额外的好处,即预取器能够在性能方面提供帮助—至少在链表的情况下是这样)

您几乎可以通过以下方式完成此操作:

类型节点结构{
值int
子节点*[2]节点
}
除了无法从底层切片获取
*[2]节点


最近有一场关于是否应该允许它切片到数组中的辩论。结果是不确定的,像你想要的东西可能会在将来添加。请记住,[4]字节是一个对象,而不是指向对象的指针。唯一可能的方法是将指针分配给数组。@fuz 7年多后,在新的Go 1.17(2021年第3季度)中,您将有一个从片到数组指针的转换:
(*[4]字节)(缓冲区)
。请参见执行[0:3]将仅复制前3个元素,而不是全部4个元素。按照另外的建议,执行[0:4]您也可以使用
copy(varLead.Magic[:],someSlice[:])
。这将复制适合数组的所有内容。只要
copy(varLead.Magic[:],someSlice)
就足够了,因为它只会复制适合数组的内容。通常情况下,切片可能比数组更受欢迎,但有些地方数组更合适、更优越。说“在每种情况下,只使用切片”是错误的。一个需要数组的示例:作为结构的成员,必须具有可比性(例如,将其用作映射键)。我使用数组表示语义,表示下面的代码不会附加到此结构中。还有性能,为什么不呢。