Приведение структуры к указанному c адресу байтового фрагмента. - PullRequest
1 голос
/ 23 марта 2020

Чтобы проанализировать огромный файл без необходимости копировать байты для каждой структуры, я хотел бы привести структуры непосредственно к адресу в срезе байтов. Это исключительно из соображений производительности. Я знаю, что могу использовать binary.Read () для создания структуры из байтового фрагмента, но, насколько я понимаю, это скопирует байты, что является дорогостоящим делом; следовательно, я хотел бы избежать использования этого подхода в этом случае использования.

Кажется, я произвел приведение структуры, поскольку получаю данные в ожидаемой структуре. Однако, когда я изменяю значения в моей структуре, они не меняют срез байта, на который должен указывать адрес структуры? Для меня важно, чтобы файл (срез байтов) отражал изменение данных в моей структуре.

Как мне заставить структуру работать с соответствующим адресом в файле?

Это демонстрация того, что я сделал так далеко:

type T struct {
    A uint8
}

func main() {

    data := []byte{0xA, 0xB, 0xC}

    // Cast bytes to struct
    pointer := *(*uint8)(unsafe.Pointer(&data[0]))
    fmt.Printf("ptr:\t0x%x\n", pointer)

    t2 := *(*T)(unsafe.Pointer(&pointer))
    fmt.Printf("T:\t0x%x\n\n", t2.A)
    // byte casting to struct ends here

    // test
    t2.A = 0x0 // I expect this to change for data as well but it does not.

    fmt.Println("AFTER CHANGE OF t2.A")
    fmt.Printf("data[0]:0x%x\n", data[0])
    fmt.Printf("ptr:\t0x%x\n\n", pointer)

    fmt.Println("WHY ARE THESE ON DIFFERENT ADDRESSES?")
    fmt.Printf("DataAddr: 0x%p\n", data)
    fmt.Printf("PtrAddr: 0x%p\n", &pointer)
    fmt.Printf("T2Addr: 0x%p\n", &t2)

}

Это печатает выход:

ptr:    0xa
T:      0xa

AFTER CHANGE OF t2.A - the original data slice has not been altered.
data[0]:0xa
ptr:    0xa

ADDRESSES
DataAddr: 0x0xc000094010
PtrAddr: 0x0xc000094013
T2Addr: 0x0xc000094030

1 Ответ

2 голосов
/ 23 марта 2020

Вы правильно конвертируете указатель, но затем разыменовываете его, что делает копию. Затем вы включаете go и берете адрес копии, конвертируете этот указатель и снова разыменовываете его (что делает другую копию). Изменение любой из копий не повлияет на другие копии.

Так что просто не разыменовывайте указатели (и в конечном итоге мы просто изменяем остроконечные значения, которые будут в той же ячейке памяти ):

data := []byte{0xA, 0xB, 0xC}

// Cast bytes to struct
pointer := (*uint8)(unsafe.Pointer(&data[0]))
fmt.Printf("ptr:\t0x%x\n", *pointer)

t2 := (*T)(unsafe.Pointer(pointer))
fmt.Printf("T:\t0x%x\n\n", t2.A)
// byte casting to struct ends here

// test
t2.A = 0x0 // This will also change data

fmt.Println("AFTER CHANGE OF t2.A")
fmt.Printf("data[0]:0x%x\n", data[0])
fmt.Printf("ptr:\t0x%x\n\n", *pointer)

fmt.Println("ADDRESSES ARE ALL THE SAME:")
fmt.Printf("DataAddr: 0x%p\n", &data[0])
fmt.Printf("PtrAddr: 0x%p\n", pointer)
fmt.Printf("T2Addr: 0x%p\n", t2)

Вывод (попробуйте на Go Playground ):

ptr:    0xa
T:  0xa

AFTER CHANGE OF t2.A
data[0]:0x0
ptr:    0x0

ADDRESSES ARE ALL THE SAME:
DataAddr: 0x0x40e020
PtrAddr: 0x0x40e020
T2Addr: 0x0x40e020
...