我需要填充一个成员类型为[8]uint8
的结构。需要使用初始化为长度8的[]byte
类型的字节数组来填充。简单的方法不起作用:
Data: [8]uint8(RequestFrame(0x180, r)),
给予
cannot convert .. (type []byte) to type [8]uint8
由于两个数组在结构上都是相同的,如果可以通过强制转换/赋值而不是复制来完成,那会很好吗?
答案 0 :(得分:2)
您可以使用byte
非常简单地将uint8
切片的内容复制到copy
数组中,如下所示:
package main
import (
"fmt"
)
func main() {
slice := []byte{1, 2, 3, 4, 5, 6, 7, 8}
array := [8]uint8{}
copy(array[:], slice)
fmt.Println(array)
}
输出
[1 2 3 4 5 6 7 8]
但是请问您为什么要使用数组?除非您有充分的理由,否则通常最好只使用切片。
答案 1 :(得分:2)
您的“简单化方法”存在的问题是
(任何类型)是一个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
指令,或最多两个),代码将
简单明了。
只有当您真正使用第二种解决方案时,才可以使用黑客手段 需要在一些关键的热路径上进行优化。 在这种情况下,请广泛评论解决方案并注意 不会意外取消引用指针。
¹此规则有一个但值得注意的例外:
[]byte
可以类型转换为string
,反之亦然。
答案 2 :(得分:1)
从 Go 1.17 开始,您可以直接使用类型转换,从 slice to an array pointer:
a := make([]byte, 8)
b := (*[8]uint8)(a) // b is pointer to [8]uint8
您可以直接取消引用以获得非指针 [8]uint8
类型。
a := make([]byte, 8)
b := *(*[8]uint8)(a) // b is [8]uint8
注意事项:
copy
不同,转换方法不会产生额外的分配(不是你的,也不是 copy
可能完成的),因为它只是产生一个指向现有的支持数组。虽然取消引用数组指针 will make a copy。a := make([]byte, 5)
b := (*[10]byte)(a) // panics
a := []byte{0xa1, 0xa2}
b := (*[2]uint8)(a)
fmt.Printf("%x\n", a[0]) // a1
b[0] = 0xff
fmt.Printf("%x\n", a[0]) // ff
byte
转换为 uint8
,包括从它们派生的类型文字,因为 byte 是 uint8 的别名(等同于)。