如何将[] byte转换为[8] uint8

只要

我需要填充一个具有type成员的结构[8]uint8这需要使用[]byte初始化为长度8 的类型的字节数组填充。简单的方法不起作用:

Data:   [8]uint8(RequestFrame(0x180, r)),

cannot convert .. (type []byte) to type [8]uint8

由于两个数组在结构上都是相同的,如果可以通过强制转换/赋值而不是复制来完成,那会很好吗?

成本:

背景

您的“简单化方法”存在的问题是(任何类型的)切片是-类型的,struct由指针和两个整数组成;指针包含基础(支持)数据数组的地址,整数包含该片的内容len()cap()内建函数返回。

换句话说,切片是数组视图的一种。

然后,在Go中,没有类型转换的概念。仅存在类型转换,并且这些转换只能在具有相同基础表示的类型之间发生¹。

由于切片和数组可能没有相同的基础表示形式(数组实际上是一个连续的内存块,大小足以容纳所有数组的元素),所以您所谓的类型转换可能不合法。

可能的解决方案

有两种可能的解决方案。

最简单的就是将数据从切片的后备数组复制到新分配的数组中:

var (
    src = []byte{1, 2, 3, 4, 5, 6, 7, 8}
    dst [8]uint8
)
copy(dst[:], src[:8])

请注意,切片类型与数组类型之间存在固有的差异:数组类型既编码其元素的类型,也对其长度进行编码(即,长度是该类型的一部分),而切片类型仅对其数组的类型进行编码元素(并且在运行时可以是任意长度)。

这意味着您可能需要在进行此类复制之前进行检查,以确保源分片恰好具有8个元素,即len(src) == len(dst)

这个不变量可能是由其他一些代码强制执行的,但是我想提醒您一下:如果src元素少于8个,则src[:8]表达式在运行时会出现恐慌,如果包含更多元素,则存在是否要复制的问题仅其中的前8个正是所需的。

第二种方法(必定更麻烦)是重新使用切片的基础数组:

import "unsafe"

var (
    src    = []byte{1, 2, 3, 4, 5, 6, 7, 8}
    dstPtr *[8]uint8
)

if len(src) != len(*dstPtr) {
    panic("boom")
}
dstPtr = (*[8]uint8)(unsafe.Pointer(&src[0]))

在这里,我们刚刚获取了切片的基础数组中包含的第一个元素的地址,并执行了“脏的”两阶段类型转换,以使所获得的指针具有类型,*[8]uint8即“数组的地址” 8 uint8s”。

请注意两个警告:

  • 现在,生成的指针指向原始切片执行的同一存储块。这意味着现在可以通过切片和我们获得的指针来改变该内存。

  • 一旦您决定将数组的数据分配给类型的变量[8]uint8(并将其作为该类型的函数的参数的参数传递),就将取消引用该指针(如使用*dstPtr),并且此时数组的数据将被复制。

    我特别提到这一点,是因为人们经常诉诸于这种骇客,就是为了不复制内存而精确地从切片中拉出后备阵列。

TL; DR

复制数据(假设已经验证了len(src) == len(dst)不变性成立之后)。

复制8个字节的速度很快(在典型的64位CPU上,这将是一条MOV指令,或者最多是两条指令),并且代码将很简单。

仅在您确实需要在某些关键的热路径上进行优化时,才诉诸于第二种解决方案中的黑客手段。在这种情况下,请广泛评论解决方案,并注意不要意外取消引用指针。


¹此规则有一个但值得注意的例外:a []byte可类型转换为string,反之亦然。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章