Функция unsafe.Offsetof

Функция unsafe.Offsetof возвращает смещение (offset) поля структуры в байтах относительно начала этой структуры. Она принимает один параметр - выражение вида struct.Field, где struct - это переменная или значение структуры, а Field - имя поля, смещение которого нужно получить. Функция возвращает значение типа uintptr, представляющее смещение поля в байтах.

Функция unsafe.Offsetof полезна при работе с низкоуровневыми операциями, такими как сериализация данных, работа с бинарными протоколами или прямое взаимодействие с памятью. Важно помнить, что смещение поля может зависеть от архитектуры компьютера и выравнивания данных, поэтому результаты могут отличаться на разных платформах.

Синтаксис

gounsafe.Offsetof(struct.Field)

Пример

Давайте получим смещение полей структуры Person:

gopackage main

import (
  "fmt"
  "unsafe"
)

type Person struct {
  Name string
  Age  int
  City string
}

func main() {
  var p Person

  nameOffset := unsafe.Offsetof(p.Name)
  ageOffset := unsafe.Offsetof(p.Age)
  cityOffset := unsafe.Offsetof(p.City)

  fmt.Printf("Name offset: %d\n", nameOffset)
  fmt.Printf("Age offset: %d\n", ageOffset)
  fmt.Printf("City offset: %d\n", cityOffset)
}

Результат выполнения кода:

go"Name offset: 0"
"Age offset: 16"
"City offset: 24"

Пример

Используем unsafe.Offsetof для получения смещения полей структуры с различными типами данных:

gopackage main

import (
  "fmt"
  "unsafe"
)

type Data struct {
  A bool
  B int64
  C float64
  D byte
}

func main() {
  var d Data

  offsetA := unsafe.Offsetof(d.A)
  offsetB := unsafe.Offsetof(d.B)
  offsetC := unsafe.Offsetof(d.C)
  offsetD := unsafe.Offsetof(d.D)

  fmt.Printf("Offset of A (bool): %d\n", offsetA)
  fmt.Printf("Offset of B (int64): %d\n", offsetB)
  fmt.Printf("Offset of C (float64): %d\n", offsetC)
  fmt.Printf("Offset of D (byte): %d\n", offsetD)
}

Результат выполнения кода:

go"Offset of A (bool): 0"
"Offset of B (int64): 8"
"Offset of C (float64): 16"
"Offset of D (byte): 24"

Пример

Используем unsafe.Offsetof вместе с unsafe.Pointer для прямого доступа к полям структуры по их смещению:

gopackage main

import (
  "fmt"
  "unsafe"
)

type User struct {
  ID   int
  Name string
}

func main() {
  user := User{ID: 42, Name: "Alice"}

  // Получаем смещение поля Name
  nameOffset := unsafe.Offsetof(user.Name)

  // Получаем указатель на структуру
  ptr := unsafe.Pointer(&user)

  // Вычисляем указатель на поле Name
  namePtr := unsafe.Pointer(uintptr(ptr) + nameOffset)

  // Читаем значение поля Name через указатель
  name := *(*string)(namePtr)
  fmt.Printf("Name field value: %s\n", name)

  // Изменяем значение поля Name
  *(*string)(namePtr) = "Bob"
  fmt.Printf("Modified user: %+v\n", user)
}

Результат выполнения кода:

go"Name field value: Alice"
"Modified user: {ID:42 Name:Bob}"

Смотрите также

  • функцию unsafe.Sizeof,
    которая возвращает размер значения в байтах
  • функцию unsafe.Alignof,
    которая возвращает выравнивание значения
  • функцию unsafe.Pointer,
    которая представляет указатель произвольного типа
  • функцию unsafe.Add,
    которая выполняет арифметику указателей