在不同类型的切片之间转换

新手上路,请多包涵

我从 UDP 套接字获得一个字节切片 ( []byte ),并希望将其视为一个整数切片 ( []int32 ),而不更改底层数组,反之亦然。在 C(++) 中,我只是在指针类型之间转换;我将如何在 Go 中执行此操作?

原文由 slartibartfast 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 689
2 个回答

正如其他人所说,投射指针在 Go 中被认为是不好的形式。以下是正确的 Go 方式和 C 数组转换的等效方式的示例。

警告:所有代码未经测试。

正确的方式

在此示例中,我们使用 encoding/binary 包将每组 4 个字节转换为 int32 。这更好,因为我们指定了字节顺序。我们也没有使用 unsafe 包来破坏类型系统。

 import "encoding/binary"

const SIZEOF_INT32 = 4 // bytes

data := make([]int32, len(raw)/SIZEOF_INT32)
for i := range data {
    // assuming little endian
    data[i] = int32(binary.LittleEndian.Uint32(raw[i*SIZEOF_INT32:(i+1)*SIZEOF_INT32]))
}

错误的方式(C 数组转换)

在这个例子中,我们告诉 Go 忽略类型系统。这不是一个好主意,因为它可能在 Go 的另一个实现中失败。它假设语言规范中没有的东西。然而,这个并没有做完整的复制。此代码使用不安全访问所有切片中通用的“SliceHeader”。标头包含指向数据(C 数组)、长度和容量的指针。我们首先需要更改长度和容量,而不是仅仅将标头转换为新的切片类型,因为如果我们将字节视为新类型,则元素会更少。

 import (
    "reflect"
    "unsafe"
)

const SIZEOF_INT32 = 4 // bytes

// Get the slice header
header := *(*reflect.SliceHeader)(unsafe.Pointer(&raw))

// The length and capacity of the slice are different.
header.Len /= SIZEOF_INT32
header.Cap /= SIZEOF_INT32

// Convert slice header to an []int32
data := *(*[]int32)(unsafe.Pointer(&header))

原文由 Stephen Weinberg 发布,翻译遵循 CC BY-SA 3.0 许可协议

你可以做你在 C 中做的事情,但有一个例外——Go 不允许从一种指针类型转换为另一种指针类型。嗯,确实如此,但是你必须使用 unsafe.Pointer 来告诉编译器你知道所有规则都被破坏了并且你知道你在做什么。这是一个例子:

 package main

import (
    "fmt"
    "unsafe"
)

func main() {
    b := []byte{1, 0, 0, 0, 2, 0, 0, 0}

    // step by step
    pb := &b[0]         // to pointer to the first byte of b
    up := unsafe.Pointer(pb)    // to *special* unsafe.Pointer, it can be converted to any pointer
    pi := (*[2]uint32)(up)      // to pointer to the first uint32 of array of 2 uint32s
    i := (*pi)[:]           // creates slice to our array of 2 uint32s (optional step)
    fmt.Printf("b=%v i=%v\n", b, i)

    // all in one go
    p := (*[2]uint32)(unsafe.Pointer(&b[0]))
    fmt.Printf("b=%v p=%v\n", b, p)
}

显然,你应该小心使用“不安全”包,因为 Go 编译器不再牵着你的手 - 例如,你可以在这里写 pi := (*[3]uint32)(up) 编译器不会抱怨,但你会遇到麻烦.

此外,正如其他人已经指出的那样,uint32 的字节在不同计算机上的布局可能不同,因此您不应假设这些是您需要的布局。

所以最安全的方法是一个一个地读取你的字节数组,并从中获取你需要的任何东西。

亚历克斯

原文由 alex 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题