いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Goメモ-382 (C言語のようにバイト配列を構造体にキャスト)

関連記事

GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ

概要

以下、自分用のメモです。

たまに、固定長のバイナリファイルなどを扱っていると、取得したバイト配列をC言語のように、そのままキャストして構造体にしてしまいたいときがあります。 cgoを使っているときなどに頻繁に利用する方法ですね。

unsafeパッケージを利用してしまうので、型セーフの部分では良くないかもしれませんが知っていると結構便利です。

サンプル

package main

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

type ST struct {
    V1    int32
    V2    uint32
    V3    [10]byte
    Dummy [6]byte
}

func (me *ST) String() string {
    return fmt.Sprintf("V1=%d, V2=%d, V3=%s, Dummy=%v\n", me.V1, me.V2, string(me.V3[:]), me.Dummy)
}

func main() {
    //
    // バイト列を作成
    //
    var (
        buf bytes.Buffer
        bin []byte
    )

    binary.Write(&buf, binary.LittleEndian, int32(127))
    binary.Write(&buf, binary.LittleEndian, uint32(255))
    buf.WriteString("helloworld")
    buf.Write(make([]byte, 6))

    bin = buf.Bytes()
    fmt.Println(hex.Dump(bin))

    //
    // 構造体にキャスト
    //
    var (
        ptr unsafe.Pointer
        st  *ST
    )

    ptr = unsafe.Pointer(&bin[0])
    st = (*ST)(ptr)

    fmt.Printf("%v\n", st)

    /*
      00000000  7f 00 00 00 ff 00 00 00  68 65 6c 6c 6f 77 6f 72  |........hellowor|
      00000010  6c 64 00 00 00 00 00 00                           |ld......|

      V1=127, V2=255, V3=helloworld, Dummy=[0 0 0 0 0 0]
   */

}

やってることは、C言語と同じでメモリ上のデータの見方を変えているだけですね。

unsafe.Pointer() すると C言語 で言う (void *) が取れます。後はそれを望みのポインタにキャストします。

当然、制限があって、メモリ上に素直にデータが並んでいるものしか出来ません。つまり基本型と配列くらいです。

スライスやマップやチャネルなどの複合型が含まれていると無理ですので、その点をご注意を。

参考情報

github.com

Goのおすすめ書籍


過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。