gpt4 book ai didi

go - 等价于 C++ reinterpret_cast a void* to a struct in Golang

转载 作者:IT王子 更新时间:2023-10-29 01:56:47 26 4
gpt4 key购买 nike

在 C++ 中,您可以从 FILE 描述符中读取数据,然后简单地将其重新解释为结构以解释数据。

在 Go 中是否有等效的方法来执行此操作?

作为一个非常人为的示例,请考虑以下内容,其中“ProcessBytes”只是一个回调,其中为您提供了一个字节数组,当从文件中读取时,这些字节数组会不断追加。

struct PayloadHeader {
uint32_t TotalPayloadLength;
uint8_t PayloadType;
};

struct TextMessage {
PayloadHeader Header;
uint32_t SenderId;
uint32_t RecieverId;
char Text[64]; // null padded
};

void ProcessBytes(const uint8_t* data, size_t dataLength) {
if(dataLength < sizeof(PayloadHeader))
return;

const PayloadHeader* header = reinterpret_cast<const PayloadHeader*>(data);
if(header.PayloadType == TEXT_MESSAGE) {
if(header.TotalLength != sizeof(TextMessage))
return;
const TextMessage* text = reinterpret_cast<const TextMessage*>(data);
// Do something with the text message~

// Adjust the *data* to 'erase' the bytes after we are done processing it
// as a TextMessage
}
}

最佳答案

现在的答案是建议使用 unsafe,但它们并没有讨论为什么你不应该使用 unsafe 以及你应该做什么。所以让我试一试。

在您在 OP 中发布的 C++ 代码中,您似乎在编写一种二进制格式,它通过简单的强制转换读取数据。简单,但有效。我能看到的唯一明显的问题是它不允许 Little Endian 和 Big Endian 之间的互操作性,但这是另一回事。

在 Go 中处理二进制编码的方法是使用方便的包 encoding/binary,它能够将二进制数据直接解码为固定大小的结构(即没有字符串或 slice ,它们是可变长度的,因此长度需要任意编码)。

以下是我将如何在 Go 中实现您的示例:

package main

import (
"bytes"
"encoding/binary"
"fmt"
)

const textMessage = 11

func main() {
// Set up our data - this is an example of the data I
// imagine you want to decode.
r := bytes.NewReader([]byte{
// Header
byte(textMessageLen + headerLen), 0, 0, 0,
11,

// Body
137, 0, 0, 0,
117, 0, 0, 0,
// Message content
'H', 'e', 'l', 'l', 'o', '!', 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
})

// We first read the header to decide what to do next.
// Notice that we explicitly pass an argument that indicates to
// parse integers using little endian, making this code portable.
var h Header
err := binary.Read(r, binary.LittleEndian, &h)
if err != nil {
fmt.Println(err)
return
}


switch h.Type {
case textMessage:
// It's a text message - make sure the length is right.
if textMessageLen != (int(h.Length) - headerLen) {
fmt.Println("Invalid payload length")
return
}

// Decode the data
var t TextMessage
err = binary.Read(r, binary.LittleEndian, &t)
if err != nil {
fmt.Println(err)
return
}

// Print it out
fmt.Printf("Sender: %d; Receiver: %d\nMessage: %s\n",
t.Sender, t.Receiver, bytes.TrimRight(t.Text[:], "\x00"))
default:
fmt.Println("unknown payload type")
}
}

// If you need to find out what the encoded size of a struct is, don't use unsafe.Sizeof;
// use binary.Size instead.
var headerLen = binary.Size(Header{})

type Header struct {
Length uint32
Type uint8
}

var textMessageLen = binary.Size(TextMessage{})

type TextMessage struct {
Sender, Receiver uint32
Text [64]byte
}

Playground

所以,这里有几点需要注意:

  • 在 Go 中,二进制格式通常从不直接从内存中读取。这是因为 1. 它依赖于平台(Little/Big endian),2. 字符串、 slice 和结构填充有问题,以及 3. 嗯,不安全。如果你不直接篡改内存,Go 几乎可以保证你的程序在任何平台上都可以流畅运行而无需任何修改。一旦你开始这样做,你就失去了这种保证。
  • 我们不需要“推进指针”我们正在读取的数据 - 我们正在传递给 binary.Readio.Reader,这意味着当我们从中读取某些内容时,读取的数据将被丢弃,因此指针会自动前进。
  • 当你自己玩内存时,GC 可能会产生影响 - GC 可能认为数据中的某个点不再被引用并且可以自由使用 - 而实际上你仍在使用它,只是没有清楚地引用使用 native Go 指针。

关于go - 等价于 C++ reinterpret_cast a void* to a struct in Golang,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52547305/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com