为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
富国沪深 2023-04-17 15:29:05
我需要填充一个具有 type 成员的结构[8]uint8。这需要用初始化为长度 8 的类型的字节数组填充。[]byte简单的方法不起作用:Data:   [8]uint8(RequestFrame(0x180, r)),给cannot convert .. (type []byte) to type [8]uint8由于这两个数组在结构上是相同的,如果这可以通过转换/赋值而不是复制来完成,那会很好吗?
查看完整描述

3 回答

?
HUWWW

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 给出了关于它的警告,并且将来可能会禁止此功能)。



查看完整回答
反对 回复 2023-04-17
?
宝慕林4294392

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]

在操场上试试吧。


但请问您为什么要使用数组?通常最好只使用切片,除非你有充分的理由。


查看完整回答
反对 回复 2023-04-17
?
翻翻过去那场雪

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 的别名(等同于)。


查看完整回答
反对 回复 2023-04-17
  • 3 回答
  • 0 关注
  • 394 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信