unsafe.Sizeof în Go

Returnează câți octeți ocupă în memorie o valoare de tipul lui x, fără memoria la care trimite (constantă la compilare).

Sintaxă
import "unsafe"

func Sizeof(x ArbitraryType) uintptr

Ce face unsafe.Sizeof

unsafe.Sizeof(x) spune câți octeți ocupă variabila x însăși. Valoarea este calculată la compilare (x nu este evaluat) și depinde de procesor: pe un sistem de 64 de biți, int și pointerii au 8 octeți.

Pentru tipurile care trimit spre alte date, rezultatul nu include acele date. Un string ocupă 16 octeți (pointer + lungime) oricât de lung ar fi textul, iar un slice 24 de octeți (pointer + lungime + capacitate), oricâte elemente ar avea.

La structuri, rezultatul include și octeții de umplutură (padding) adăugați ca fiecare câmp să fie aliniat. Ordinea câmpurilor poate schimba mărimea structurii. Funcțiile înrudite unsafe.Alignof și unsafe.Offsetof dau alinierea și poziția unui câmp. Pachetul unsafe ocolește siguranța tipurilor; Sizeof este însă inofensivă, doar citește o informație.

Parametri

Parametru Descriere
x O expresie de orice tip; contează doar tipul ei.

Ce returnează

Mărimea în octeți, ca uintptr.

Exemple

Exemplul 1: Mărimea tipurilor de bază

Rezultatul este pentru un sistem de 64 de biți. Textul lung și slice-ul cu un milion de elemente au aceeași mărime ca variantele scurte.

package main

import (
    "fmt"
    "strings"
    "unsafe"
)

func main() {
    var (
        b   bool
        i8  int8
        i   int
        f   float64
        p   *int
        s   = "Iași"
        lung = strings.Repeat("a", 1000)
        sl  = make([]int, 1_000_000)
        m   = map[string]int{}
    )
    fmt.Println("bool:", unsafe.Sizeof(b))
    fmt.Println("int8:", unsafe.Sizeof(i8))
    fmt.Println("int:", unsafe.Sizeof(i))
    fmt.Println("float64:", unsafe.Sizeof(f))
    fmt.Println("*int:", unsafe.Sizeof(p))
    fmt.Println("string:", unsafe.Sizeof(s), unsafe.Sizeof(lung))
    fmt.Println("[]int:", unsafe.Sizeof(sl))
    fmt.Println("map:", unsafe.Sizeof(m))
}

Rezultat

bool: 1
int8: 1
int: 8
float64: 8
*int: 8
string: 16 16
[]int: 24
map: 8

Exemplul 2: Ordinea câmpurilor schimbă mărimea unei structuri

În prima structură, după fiecare bool se adaugă 7 octeți de umplutură ca int64 să înceapă la un multiplu de 8. Grupând câmpurile mici, structura scade de la 24 la 16 octeți.

package main

import (
    "fmt"
    "unsafe"
)

type Risipitor struct {
    Activ  bool
    Sold   int64
    Admin  bool
}

type Compact struct {
    Sold   int64
    Activ  bool
    Admin  bool
}

func main() {
    fmt.Println("Risipitor:", unsafe.Sizeof(Risipitor{}))
    fmt.Println("Compact:  ", unsafe.Sizeof(Compact{}))
    var r Risipitor
    fmt.Println("Sold începe la octetul", unsafe.Offsetof(r.Sold), "cu alinierea", unsafe.Alignof(r.Sold))
}

Rezultat

Risipitor: 24
Compact:   16
Sold începe la octetul 8 cu alinierea 8

Greșeli frecvente

  • Folosești unsafe.Sizeof ca să afli câtă memorie ocupă un slice sau un text cu tot cu conținut: primești doar mărimea antetului (24, respectiv 16 octeți). Pentru conținut calculezi len(s) * unsafe.Sizeof(s[0]), respectiv len(text).
  • Presupui aceleași valori pe toate sistemele: pe 32 de biți, int și pointerii au 4 octeți.

Comenzi înrudite

  • math.Float64bits: Transformă un float64/float32 în biții lui IEEE 754 (uint64/uint32) și înapoi, fără nicio conversie a valorii.
  • slices.Grow: Grow rezervă loc pentru încă n elemente fără realocări ulterioare; Clip elimină capacitatea nefolosită.