Zašto strukture imaju rupe
Svaki skalarni tip ima poravnanje: na većini ABI-ja 4-bajtni int mora da stoji na adresi deljivoj sa 4, 8-bajtni double ili pokazivač na adresi deljivoj sa 8. Kompajler ubacuje popunjavanje (padding) ispred člana da bi dostigao njegovo poravnanje, i na kraju, da bi nizovi strukture držali svaki element poravnatim (poravnanje same strukture je poravnanje njenog najstrože poravnatog člana). struct { char c; int i; } zato ima 8 bajtova, a ne 5, a struct { char a; long b; char c; } ima 24 bajta, dok { long b; char a; char c; } ima 16. Ređanje članova od najvećeg poravnanja ka najmanjem uklanja većinu popunjavanja, što je ono što radi predlog preuređivanja.
Šta se razlikuje između ABI-ja
| Tip | x86-64 SysV | AArch64 | i386 | ARM32 | MSVC x64 |
|---|---|---|---|---|---|
| long | 8 | 8 | 4 | 4 | 4 |
| pointer | 8 | 8 | 4 | 4 | 8 |
| double | 8, poravnanje 8 | 8, poravnanje 8 | 8, poravnanje 4 | 8, poravnanje 8 | 8, poravnanje 8 |
| long long | poravnanje 8 | poravnanje 8 | poravnanje 4 | poravnanje 8 | poravnanje 8 |
| long double | 16, poravnanje 16 | 16, poravnanje 16 | 12, poravnanje 4 | 8 | 8 |
| wchar_t | 4 | 4 | 4 | 4 | 2 |
| pravila za bit polja | Itanium: polje se smešta na sledeći bit osim ako bi prešlo prozor od sizeof(type) bajtova; neimenovana polja ne utiču na poravnanje (x86) ili utiču (ARM) | svaki niz polja iste veličine dobija sopstvenu jedinicu skladištenja; promena tipa započinje novu jedinicu | |||
Isti izvorni kod zato može da proizvede različite binarne rasporede na različitim platformama, zbog čega strukture koje idu preko mreže ili u datoteke treba da koriste tipove fiksne širine, eksplicitne članove za popunjavanje i static_assert(sizeof(struct x) == 24).
Bit polja
Susedna bit polja se pakuju u jedinicu skladištenja svog deklarisanog tipa. Kod GCC-a i Clanga polje ide na sledeći slobodan bit osim ako bi prelazilo prozor od sizeof(type) bajtova, u kom slučaju se pomera na sledeću granicu poravnanja; polje nulte širine (int :0) prisiljava sledeće polje u novu jedinicu. MSVC dodeljuje novu jedinicu kad god se veličina tipa promeni, pa int a:8; char b:8; zauzima 5 bajtova na MSVC-u, a 2 na GCC-u. Redosled bitova unutar jedinice prati endianness platforme (najmanje značajni bitovi prvi na little-endian), zbog čega su bit polja loš način za opisivanje formata na žici.
packed, aligned i #pragma pack
__attribute__((packed)) uklanja sve popunjavanje (članovi mogu biti neporavnati, što je sporo na x86 i izaziva grešku na nekim ARM konfiguracijama). #pragma pack(n) ograničava poravnanje članova na n bajtova. __attribute__((aligned(n))) i _Alignas(n) povećavaju poravnanje, obično na 16 za SIMD ili 64 za keš liniju. Sve ovo je prikazano u rasporedu.
Pitanja
Koliko je ovo tačno?
Mehanizam je testiran prema clang -fdump-record-layouts za x86-64, i386, AArch64, ARM32 i MSVC x64 na skupu struktura sa bit poljima, pakovanjem, ugnežđenim unijama i poljima nulte širine. GCC i Clang se na ovim ABI-jima međusobno slažu. Za egzotične slučajeve (prekomerno poravnati tipovi u bit poljima, __int128 na 32 bita, vektorski tipovi) proverite sa kompajlerom.
Kako da dobijem prave vrednosti iz svog kompajlera?
clang -Xclang -fdump-record-layouts -c file.c ispisuje svaku strukturu; GCC: kompajlirajte sa -g i pokrenite pahole nad objektnom datotekom, ili koristite offsetof() i sizeof() u malom programu. Dodajte static_assert(offsetof(struct s, x) == 8) da zaključate raspored.
Kaže da je tip nepoznat.
Tipovi iz sistemskih zaglavlja (size_t, uint32_t, time_t i Windows DWORD porodica) su ugrađeni; za sve ostalo je potrebno nalepiti typedef iznad strukture koja ga koristi. Nepoznato ime se tretira kao 4-bajtni int i označava.
Može li da dekodira strukturu iz core dumpa ili paketa?
Nalepite bajtove u odeljak za dekodiranje. Celi brojevi, float brojevi, char, pokazivači, char nizovi i bit polja prikazuju se sa redosledom bajtova koji izaberete; ugnežđene strukture se navode, ali ne razvijaju (izaberite unutrašnju strukturu da je dekodirate zasebno).
Zašto preuređivanje ne pomaže mojoj strukturi?
Ako su članovi već sortirani po poravnanju, ili struktura ima bit polja ili anonimne unije (koje se ne preuređuju automatski), predlog se ne prikazuje. Popunjavanje na kraju je neizbežno kada poravnanje najvećeg člana ne deli zbir veličina.