在Golang中,如何将切片转换为数组

eka*_*aqu 26 go

我是Go的新手并试图编写一个读取RPM文件的应用程序.每个块的开头都有一个Magic char [4]byte.

这是我的结构

type Lead struct {
  Magic        [4]byte
  Major, Minor byte
  Type         uint16
  Arch         uint16
  Name         string
  OS           uint16
  SigType      uint16
}
Run Code Online (Sandbox Code Playgroud)

我正在尝试执行以下操作:

lead := Lead{}
lead.Magic = buffer[0:4]
Run Code Online (Sandbox Code Playgroud)

我在网上搜索,不知道如何从切片到数组(不复制).我可以随时制作魔法[]byte(甚至是魔法uint64),但我更好奇的是[]byte,[4]byte如果需要,我将如何从类型转到?

twi*_*inj 35

内置方法副本仅将切片复制到切片而不是切片到数组.

你必须欺骗复制,认为数组是一个切片

copy(varLead.Magic[:], someSlice[0:4])
Run Code Online (Sandbox Code Playgroud)

或者使用for循环来执行复制:

for index, b := range someSlice {

    varLead.Magic[index] = b

}
Run Code Online (Sandbox Code Playgroud)

或者像zupa使用文字一样.我已经添加了他们的工作示例.

去游乐场

  • 好吧只是`copy(varLead.Magic [:],someSlice)`就足够了,因为它只会复制适合数组的内容. (2认同)

Dus*_*tin 11

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

看看copy内置如何做到这一点.


Kev*_*uan 7

试试这个:

copy(lead.Magic[:], buf[0:4])
Run Code Online (Sandbox Code Playgroud)


Von*_*onC 6

Tapir LiuiGo101 导演推特

1.18 1.191.20 将支持从切片到数组的转换:golang/goissues 46505

因此,从 Go 1.18 开始,切片 copy2 实现可以写为:

*(*[N]T)(d) = [N]T(s)
Run Code Online (Sandbox Code Playgroud)

或者,如果允许转换以 L 值表示,则更简单:

[N]T(d) = [N]T(s)
Run Code Online (Sandbox Code Playgroud)

无需复制,您可以在下一个 Go 1.17(2021 年第 3 季度)中将切片转换为数组指针

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

https://blog.golang.org/slices-intro/slice-1.png

请参阅golang/go问题 395:spec: convert slice x into array pointer,现在使用CL 216424/实现,并提交 1c26843

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

*(*[N]T)(d) = [N]T(s)
Run Code Online (Sandbox Code Playgroud)

所以在你的情况下,提供的Magic类型是*[4]byte

lead.Magic = (*[4]byte)(buffer)
Run Code Online (Sandbox Code Playgroud)

注意:类型别名也可以工作:

[N]T(d) = [N]T(s)
Run Code Online (Sandbox Code Playgroud)

为什么要转换为数组指针?正如第 395 期所述:

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

像这样的函数:

s := make([]byte, 2, 4)
s0 := (*[0]byte)(s)      // s0 != nil
s2 := (*[2]byte)(s)      // &s2[0] == &s[0]
s4 := (*[4]byte)(s)      // panics: len([4]byte) > len(s)

var t []string
t0 := (*[0]string)(t)    // t0 == nil
t1 := (*[1]string)(t)    // panics: len([1]string) > len(s)
Run Code Online (Sandbox Code Playgroud)

可以变成:

lead.Magic = (*[4]byte)(buffer)
Run Code Online (Sandbox Code Playgroud)

允许编译器仅检查一次所有边界,并给出有关超出范围索引的编译时错误。

一个常用的示例是使树节点或链表节点的类尽可能小,这样您就可以将尽可能多的类塞入 L1 缓存行中。
这是通过每个节点具有指向左子节点的单个指针,并且通过指向左子节点的指针+1来访问右子节点来完成的。
这为右节点指针节省了8个字节。

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

几乎可以在 Go 中做到这一点:

type A [4]int
var s = (*A)([]int{1, 2, 3, 4})
Run Code Online (Sandbox Code Playgroud)

除了无法*[2]node从底层切片中获取 a 之外。


Go 1.20(2023 年第一季度):通过CL 430415428938 (type)430475 (reflect)429315 (spec)解决此问题。