我需要填充一个具有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 uint8
s”。
请注意两个警告:
现在,生成的指针指向原始切片执行的同一存储块。这意味着现在可以通过切片和我们获得的指针来改变该内存。
一旦您决定将数组的数据分配给类型的变量[8]uint8
(并将其作为该类型的函数的参数的参数传递),就将取消引用该指针(如使用*dstPtr
),并且此时数组的数据将被复制。
我特别提到这一点,是因为人们经常诉诸于这种骇客,就是为了不复制内存而精确地从切片中拉出后备阵列。
复制数据(假设已经验证了len(src) == len(dst)
不变性成立之后)。
复制8个字节的速度很快(在典型的64位CPU上,这将是一条MOV
指令,或者最多是两条指令),并且代码将很简单。
仅在您确实需要在某些关键的热路径上进行优化时,才诉诸于第二种解决方案中的黑客手段。在这种情况下,请广泛评论解决方案,并注意不要意外取消引用指针。
¹此规则有一个但值得注意的例外:a []byte
可类型转换为string
,反之亦然。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句