57

UDPソケットからバイトスライス([]byte)を取得し、基になる配列を変更せずに整数スライス([]int32)として扱いたい、またはその逆。C(++)では、ポインタ型間でキャストするだけです。Goでこれをどのように行うのですか?

4

9 に答える 9

56

他の人が言っているように、ポインタをキャストすることは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))
于 2012-08-13T01:55:34.130 に答える
9

1つの例外を除いて、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のバイトは、コンピューターによってレイアウトが異なる可能性があるため、必要に応じてこれらがレイアウトであると想定しないでください。

したがって、最も安全なアプローチは、バイトの配列を1つずつ読み取り、それらから必要なものを作成することです。

アレックス

于 2012-08-13T00:10:17.000 に答える
8

簡単な答えはあなたができないということです。あるタイプのスライスを別のタイプのスライスにキャストすることはできません。配列をループして、配列内の各アイテムをキャストしながら、必要なタイプの別の配列を作成します。型安全性はgoの重要な機能であるため、これは一般的に良いことと見なされています。

于 2012-08-12T19:33:35.110 に答える
2

サイズが不明な問題があり、以前の安全でないメソッドを次のコードで微調整しました。与えられたバイトスライスb..。

int32 slice is (*(*[]int)(Pointer(&b)))[:len(b)/4]

配列からスライスへの例には、架空の大きな定数が与えられ、配列が割り当てられていないため、同じ方法でスライス境界が使用されます。

于 2013-07-29T18:32:07.257 に答える
1

あなたは「安全でない」パッケージでそれをすることができます

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    var b [8]byte = [8]byte{1, 2, 3, 4, 5, 6, 7, 8}
    var s *[4]uint16 = (*[4]uint16)(unsafe.Pointer(&b))
    var i *[2]uint32 = (*[2]uint32)(unsafe.Pointer(&b))
    var l *uint64 = (*uint64)(unsafe.Pointer(&b))

    fmt.Println(b)
    fmt.Printf("%04x, %04x, %04x, %04x\n", s[0], s[1], s[2], s[3])
    fmt.Printf("%08x, %08x\n", i[0], i[1])
    fmt.Printf("%016x\n", *l)
}

/*
 * example run:
 * $ go run /tmp/test.go
 * [1 2 3 4 5 6 7 8]
 * 0201, 0403, 0605, 0807
 * 04030201, 08070605
 * 0807060504030201
 */
于 2016-10-11T17:57:51.293 に答える
1

以前の回答が出されたときには利用できなかったかもしれませんが、このbinary.Read方法は上記の「正しい方法」よりも優れた回答であるように思われます。

この方法では、バイナリデータをリーダーから目的のタイプの値またはバッファに直接読み取ることができます。これを行うには、バイト配列バッファー上にリーダーを作成します。または、バイト配列を提供するコードを制御できる場合は、暫定バイト配列を必要とせずに、コードを置き換えてバッファーに直接読み込むことができます。

ドキュメントとちょっとした例については、https://golang.org/pkg/encoding/binary/#Readを参照してください。

于 2019-03-06T16:50:50.940 に答える
1

unsafeGo 1.17以降、パッケージを使用してこれを行うためのより簡単な方法があります。

import (
    "unsafe"
)

const SIZEOF_INT32 = unsafe.Sizeof(int32(0)) // 4 bytes

func main() {
    var bs []byte
    
    // Do stuff with `bs`. Maybe do some checks ensuring that len(bs) % SIZEOF_INT32 == 0
    
    data := unsafe.Slice((*int32)(unsafe.Pointer(&bs[0])), len(bs)/SIZEOF_INT32)

    // A more verbose alternative requiring `import "reflect"`
    // data := unsafe.Slice((*int32)(unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(&bs)).Data)), len(bs)/SIZEOF_INT32)
}
于 2021-08-25T21:17:40.143 に答える
1

1.17以降に移動

Go 1.17は、まさにこれを行う関数を導入しました。unsafe.Slice

[]byteaを[]int32:に変換する

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    theBytes := []byte{
        0x33, 0x44, 0x55, 0x66,
        0x11, 0x22, 0x33, 0x44,
        0x77, 0x66, 0x55, 0x44,
    }

    numInts := uintptr(len(theBytes)) * unsafe.Sizeof(theBytes[0]) / unsafe.Sizeof(int32(0))
    theInts := unsafe.Slice((*int32)(unsafe.Pointer(&theBytes[0])), numInts)

    for _, n := range theInts {
        fmt.Printf("%04x\n", n)
    }
}

遊び場

于 2021-10-22T11:29:35.203 に答える
0

http://play.golang.org/p/w1m5Cs-ecz

package main

import (
    "fmt"
    "strings"
)

func main() {
    s := []interface{}{"foo", "bar", "baz"}
    b := make([]string, len(s))
    for i, v := range s {
        b[i] = v.(string)
    }
    fmt.Println(strings.Join(b, ", "))
}
于 2014-05-19T10:30:15.427 に答える