Konwertuj między plastrami różnych typów

Dostaję kawałek bajtowy ([]byte) z gniazda UDP i chcę traktować go jako kawałek całkowity ([]int32) bez zmiany podstawowej tablicy i odwrotnie. W C (++) po prostu rzuciłbym między typami wskaźników; jak to zrobić w Go?

Author: slartibartfast, 2012-08-12

6 answers

Jak mówili inni, rzucanie wskaźnika jest uważane za złą formę w Go. Oto przykłady właściwej drogi Go i odpowiednika rzutu tablicy C.

Uwaga: cały kod jest niesprawdzony.

The Right Way

W tym przykładzie używamy pakietu encoding/binary do konwersji każdego zbioru 4 bajtów na int32. Jest to lepsze, ponieważ określamy endianness. Nie używamy również pakietu unsafe do łamania systemu typów.

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]))
}

The Wrong Way (C array casting)

W tym przykładzie mówimy, aby Go zignorował system typów. Nie jest to dobry pomysł, ponieważ może zawieść w innej implementacji Go. Zakłada, że rzeczy nie mieszczą się w specyfikacji języka. Ten jednak nie wykonuje pełnej kopii. Ten kod używa niebezpiecznego dostępu do "SliceHeader", który jest powszechny we wszystkich plasterkach. Nagłówek zawiera wskaźnik do danych (tablica C), długość i pojemność. Zamiast konwertować nagłówek do nowego typu slice, najpierw musimy aby zmienić długość i pojemność, ponieważ jest mniej elementów, jeśli traktujemy bajty jako nowy typ.

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))
 33
Author: Stephen Weinberg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-13 04:50:37

Krótka odpowiedź jest taka, że nie możesz. idź i nie pozwól, aby rzucać plasterek jednego typu na plasterek innego typu. Będziesz mieć pętlę przez tablicę i utworzyć inną tablicę typu, który chcesz podczas odlewania każdego elementu w tablicy. Jest to ogólnie uważane za dobrą rzecz, ponieważ typówbezpieczeństwo jest ważną cechą go.

 7
Author: Jeremy Wall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-12 19:33:35

Robisz to, co robisz w C, z jednym wyjątkiem-Go nie pozwala na konwersję z jednego typu wskaźnika na inny. Tak, ale musisz użyć niebezpiecznego.Wskaźnik mówi kompilatorowi, że jesteś świadomy, że wszystkie reguły są łamane i wiesz, co robisz. Oto przykład:

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

OczywiĹ "cie naleĹźy uwaĺźaä ‡ na uĹźywanie pakietu" unsafe", poniewaĹź Go kompilator nie trzyma juĹź za rÄ ™ kÄ ™ - na przykĹ 'ad moĹźesz tu napisaÄ ‡ pi := (*[3]uint32)(up) i kompilator by nie narzekaĺ', ale będzie w tarapatach.

Również, jak już zauważyli inni, bajty uint32 mogą być układane inaczej na różnych komputerach, więc nie powinieneś zakładać, że są to układy tak, jak ich potrzebujesz.

Najbezpieczniejszym podejściem byłoby odczytanie tablicy bajtów jeden po drugim i zrobienie z nich wszystkiego, czego potrzebujesz.

Alex

 5
Author: alex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-13 00:24:04

Miałem problem z nieznanym rozmiarem i poprawiłem poprzednią metodę niebezpieczną poniższym kodem. podany kawałek bajtu b ...

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

W przykładzie array to slice może być podana fikcyjna duża stała i granice slice używane w ten sam sposób, ponieważ żadna tablica nie jest przydzielana.

 2
Author: Tony Wilson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-30 10:10:22

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, ", "))
}
 0
Author: KIM Taegyoon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-19 10:30:15

Możesz to zrobić za pomocą pakietu "unsafe"

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
 */
 0
Author: Matteo Croce,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-11 17:57:51