Предисловие: Несмотря на то, что вы хотите, старайтесь избегать использования пакета unsafe
в максимально возможной степени. Это не решение «идти вперёд», а скорее рассматривать его как последнее средство (или что-то, что приходит после этого).
Go действительно дает вам поддержку для этого в пакете unsafe
.
Даже спецификация имеет выделенный раздел для этого. Spec: Package unsafe
:
Встроенный пакет unsafe
, известный компилятор, предоставляет средства для низкоуровневого программирования, включая операции, которые нарушают систему типов. Пакет с использованием unsafe
должен быть проверен вручную для безопасности типа и не может быть переносимым.
Пакет был намеренно назван unsafe
, что дает вам правильное предварительное предупреждение о том,
Что вам нужно, это тип unsafe.Pointer
. Pointer
является специальным pointer type, который может быть не dereferenced, но любой тип указателя может быть преобразован в Pointer
, а Pointer
может быть преобразован в любой (другой) тип указателя. Так что это ваш «шлюз» между разными типами.
Например, если у вас есть значение типа float64
(который составляет 8 байт в памяти), можно интерпретировать эти 8 байт в виде int64
(который также 8 байт в памяти), как это:
var f float64 = 1
var i int64
ip := (*int64)(unsafe.Pointer(&f))
i = *ip
fmt.Println(i)
Output (попробуйте на Go Playground):
4607182418800017408
ключ эта строка:
(*int64)(unsafe.Pointer(&f))
Это значит взять адрес f
(который будет типа *float64
), преобразовать его в unsafe.Pointer
(любой тип указателя может быть преобразован в Pointer
), а затем преобразовать эту unsafe.Pointer
значения другого типа указателя *int64
. Если мы разыграем этот указатель, получим значение типа int64
.
В вашем примере вы хотите «поместить» переменную на адрес с примененным смещением. Go не имеет арифметики указателя. Вы можете обойти это 2-мя способами:
использование uintptr
, который может содержать адрес, но вы можете рассматривать его как int
и добавить значения к нему
или использовать указатель на «буфер типа, например *[1<<31]byte
; вы можете преобразовать адрес в этот указатель, и вы можете применить смещение путем индексации буфера с заданным смещением и взять адрес этого элемента, например. Метод &buf[14]
# 1 может выглядеть это нравится:
type X [16]byte
var x = X{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
xx := (uintptr)(unsafe.Pointer(&x[0])) + 14 // APPLY OFFSET
var x2 X = *(*X)(unsafe.Pointer(xx))
fmt.Println(x2[0], x2[1])
Output (попробовать его на Go Playground):
14 15
Метод # 2 может выглядеть следующим образом:
type X [16]byte
var x = X{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
xx := (*X)(unsafe.Pointer(&x[0]))
xx = (*X)(unsafe.Pointer(&xx[14])) // APPLY OFFSET
var x2 X = *(*X)(unsafe.Pointer(xx))
fmt.Println(x2[0], x2[1])
Результат такой же. Попробуйте на Go Playground.
@soundslikeodd Вы можете отредактировать еще раз? Извините – overexchange
на самом деле это может быть небезопасно в C ... –
Код C делает разные вещи на разных компиляторах и машинах из-за различий в ограничениях выравнивания, выравнивания, отступов и контентов. Это может даже вызвать segfault. Безопасная, переносная вещь, которую нужно сделать, - это извлечь поле за раз, в противоположность тому, что [этот ответ] (http://stackoverflow.com/a/39132179/589924). – ikegami