3 回答
TA贡献1874条经验 获得超12个赞
背景
您的“简单方法”的问题在于 slice (任何类型)是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]表达式将在运行时出现 panic,如果包含更多,那么就会有是否复制的问题只是其中的前 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” 8uint8秒”。
注意两个注意事项:
结果指针现在指向与原始切片相同的内存块。这意味着现在可以通过切片和我们获得的指针来改变该内存。
一旦您决定将数组的数据分配给类型的变量[8]uint8(并将其作为参数传递给该类型的函数参数),您将取消引用该指针(如 with *dstPtr),并且此时 数组的数据将被复制。
我特别提到这一点,因为人们经常诉诸像这样的 hack 来精确地将支持阵列从切片中拉出,以试图不复制内存。
长话短说
复制数据(在假设验证 len(src) == len(dst)不变保持之后)。
复制 8 个字节很快(在典型的 64 位 CPU 上,这将是一条MOV指令,最多两条),代码也很简单。
只有当你真的需要优化一些关键的热路径时,才使用第二种解决方案。在这种情况下,对解决方案进行广泛注释并注意不要意外取消引用您的指针。
¹ 此规则有明显的例外情况:
A[]byte可以类型转换为string,反之亦然。
Astring可以类型转换为[]rune,反之亦然。
Anint是类型可转换的string(但由于 Go 1.15go vet 给出了关于它的警告,并且将来可能会禁止此功能)。
TA贡献2021条经验 获得超8个赞
您可以使用 非常简单地将切片的内容复制byte到数组中,如下所示:uint8copy
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]
在操场上试试吧。
但请问您为什么要使用数组?通常最好只使用切片,除非你有充分的理由。
TA贡献2065条经验 获得超13个赞
从 Go 1.17 开始,您可以直接使用类型转换,从切片到数组指针:
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),因为它只是生成一个指向现有后备数组的指针。尽管取消引用数组指针会生成一个副本。
如果数组的长度大于切片的长度,则转换会出现混乱
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
您可以将 from 转换byte为uint8,包括从它们派生的类型文字,因为 byte 是uint8 的别名(等同于)。
- 3 回答
- 0 关注
- 370 浏览
添加回答
举报