Подтвердить что ты не робот

Преобразование между кусочками разных типов

Я получаю байтовый фрагмент ([]byte) из сокета UDP и хочу рассматривать его как целочисленный фрагмент ([]int32) без изменения базового массива и наоборот. В C (++) я просто использовал бы типы указателей; как я могу сделать это в Go?

4b9b3361

Ответ 1

Как говорили другие, приведение указателя в 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))

Ответ 2

Короткий ответ: вы не можете. Идите, не позволяйте бросать кусочек одного типа на кусочек другого типа. У вас будет цикл через массив и создайте другой массив того типа, который вы хотите, при кастовании каждого элемента в массиве. Это обычно считается хорошей вещью, так как типы безопасности являются важной особенностью go.

Ответ 3

Вы выполняете то, что вы делаете на 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 могут отличаться друг от друга на разных компьютерах, поэтому вы не должны предполагать, что они являются макетом по своему усмотрению.

Таким образом, самым безопасным подходом было бы прочитать ваш массив байтов один за другим и сделать все, что вам нужно.

Алекс

Ответ 4

У меня была проблема с неизвестным размером и изменил предыдущий небезопасный метод с помощью следующего кода. заданный байтовый срез b...

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

Пример массива на срез может быть задан как вымышленная большая константа, так и границы среза, используемые таким же образом, поскольку не выделен массив.

Ответ 5

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, ", "))
}

Ответ 6

Вы можете сделать это с помощью "небезопасного" пакета

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
 */