等效于C ++ reinterpret_cast void *到Golang中的结构

时间:2018-09-28 02:03:47

标签: go

在C ++中,您可以从FILE描述符中读取数据,然后只需重新解释_cast到结构中即可解释数据。

在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
  }
}

2 个答案:

答案 0 :(得分:2)

目前,答案是unsafe,但它们并没有说明为什么不应该使用unsafe以及应该做什么。因此,让我尝试一下。

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

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

这是我在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.字符串,切片和结构填充存在麻烦; 3.它很不安全。如果您不直接篡改内存,Go几乎可以保证您的程序无需任何修改即可在任何平台上顺利运行。开始这样做的那一刻,您就失去了保证。
  • 我们不需要“超前指针”来读取正在读取的数据-我们将传递给binary.Readio.Reader,这意味着当我们从中读取内容时,读取的数据将被丢弃,因此指针将自动前进。
  • 当您自己玩内存时,GC可能会产生一些影响-GC可能认为数据中的某个点不再被引用并且可以自由使用-而实际上您仍在使用它,只是没有明确引用使用本地Go指针。

答案 1 :(得分:1)

可以,但是您不应该这样做,因为它不安全。 Golang未定义struct的内存模型(据我所知),它可能会在golang版本之间改变。经常进行元帅/非元帅。

package main

import (
    "fmt"
    "unsafe"
)

type mys struct{
    a int
}

func main() {
    v := mys{0x1234abcd}
    unsafeArrayP := (*[unsafe.Sizeof(v)]byte)(unsafe.Pointer(&v))
    for i:=0;i<4;i++ {
        fmt.Printf("%x,", unsafeArrayP[i])
    }

    v2 := 0x1234abcd
    v3 := *((*mys)(unsafe.Pointer(&v2)))
    fmt.Println(v == v3)
}

打印cd,ab,34,12,true