Rozważać:
struct mystruct_A
{
char a;
int b;
char c;
} x;
struct mystruct_B
{
int b;
char a;
} y;
Rozmiary struktur wynoszą odpowiednio 12 i 8.
Czy te struktury są wyściełane czy zapakowane?
Kiedy ma miejsce wypełnianie lub pakowanie?
padding
czyni rzeczy większymi.packing
zmniejsza rzeczy. Zupełnie inaczej.Odpowiedzi:
Wypełnienie wyrównuje elementy struktury do „naturalnych” granic adresów - powiedzmy,
int
członkowie mieliby przesunięcia, które sąmod(4) == 0
na platformie 32-bitowej. Padding jest domyślnie włączony. Wstawia następujące „luki” do pierwszej struktury:Z drugiej strony, pakowanie uniemożliwia kompilatorowi wypełnianie - należy tego wyraźnie zażądać - w GCC to
__attribute__((__packed__))
, więc:tworzy strukturę wielkości
6
w architekturze 32-bitowej.Uwaga: niezaangażowany dostęp do pamięci jest wolniejszy na architekturach, które na to pozwalają (jak x86 i amd64), i jest wyraźnie zabroniony na architekturach ścisłego wyrównania, takich jak SPARC.
źródło
( Powyższe odpowiedzi wyjaśniły powód dość wyraźnie, ale nie wydają się całkowicie jasne co do wielkości wypełnienia, więc dodam odpowiedź zgodnie z tym, czego nauczyłem się z Zaginionej sztuki pakowania struktury , ewoluowała, aby nie ograniczać się do
C
, ale również zastosowanieGo
,Rust
. )Wyrównanie pamięci (dla struct)
Zasady:
np. w systemie 64-bitowym,
int
powinien zaczynać się pod adresem podzielnym przez 4, along
przez 8short
przez 2.char
ichar[]
są wyjątkowe, mogą być dowolnym adresem pamięci, więc nie wymagają przed nimi wypełniania.struct
, poza potrzebą wyrównania dla każdego poszczególnego elementu, rozmiar całej struktury zostanie wyrównany do rozmiaru podzielnego przez rozmiar największego pojedynczego elementu, przez wypełnienie na końcu.np. jeśli największy członek struct jest
long
podzielny przez 8,int
następnie przez 4,short
a następnie przez 2.Zamówienie członka:
stu_c
istu_d
z poniższego przykładu mają te same elementy, ale w innej kolejności i dają różne rozmiary dla 2 struktur.Adres w pamięci (dla struct)
Zasady:
Adres strukturalny zaczyna się od
(n * 16)
bajtów. ( W poniższym przykładzie widać, że wszystkie wydrukowane adresy szesnastkowe struktur kończą się na0
. )Powód : możliwy największy pojedynczy element struktury ma 16 bajtów (
long double
).char
członka, jej adres może zaczynać się pod dowolnym adresem.Puste miejsce :
Np .
test_struct_address()
Poniżej, zmiennax
znajduje się pomiędzy sąsiednimi strukturamig
ih
.Bez względu na
x
to, czy zostanie zadeklarowany,h
adres się nie zmieni,x
wystarczy ponownie wykorzystać puste miejsce, któreg
zmarnowało się.Podobna sprawa dla
y
.Przykład
( dla systemu 64-bitowego )
memory_align.c :
Wynik wykonania -
test_struct_padding()
:Wynik wykonania -
test_struct_address()
:Zatem początek adresu dla każdej zmiennej to g: d0 x: dc h: e0 y: e8
źródło
<The Lost Art of C Structure Packing>
dość dobrze wyjaśnia zasady, nawet że jest trochę dłuższa niż ta odpowiedź. Książka jest dostępna bezpłatnie online: catb.org/esr/structure-packingWiem, że to pytanie jest stare i większość odpowiedzi tutaj wyjaśnia padding naprawdę dobrze, ale starając się to zrozumieć, doszedłem do wniosku, że posiadam „wizualny” obraz tego, co się dzieje.
Procesor odczytuje pamięć w „kawałkach” o określonym rozmiarze (słowie). Powiedz, że słowo procesora ma 8 bajtów. Będzie patrzył na pamięć jako duży rząd 8 bajtów bloków konstrukcyjnych. Za każdym razem, gdy musi pobrać jakieś informacje z pamięci, dotrze do jednego z tych bloków i je zdobędzie.
Jak wydaje się na powyższym obrazku, nie ma znaczenia, gdzie jest Char (1 bajt), ponieważ będzie on w jednym z tych bloków, wymagając od CPU przetworzenia tylko 1 słowa.
Kiedy mamy do czynienia z danymi większymi niż jeden bajt, takimi jak 4-bajtowy int lub 8-bajtowy podwójny, sposób, w jaki są one wyrównane w pamięci, wpływa na to, ile słów będzie musiało przetworzyć procesor. Jeśli 4-bajtowe fragmenty są wyrównane w taki sposób, że zawsze pasują do wnętrza bloku (adres pamięci jest wielokrotnością 4), tylko jedno słowo będzie musiało zostać przetworzone. W przeciwnym razie fragment 4 bajtów może mieć część siebie w jednym bloku, a część w innym, wymagając od procesora przetworzenia 2 słów w celu odczytania tych danych.
To samo odnosi się do 8-bajtowego podwójnego, z tym wyjątkiem, że musi on znajdować się w wielokrotności adresu pamięci 8, aby zagwarantować, że zawsze będzie znajdować się w bloku.
Dotyczy to 8-bajtowego edytora tekstu, ale ta koncepcja ma zastosowanie do innych rozmiarów słów.
Wypełnianie polega na wypełnianiu luk między tymi danymi, aby upewnić się, że są one wyrównane z tymi blokami, co poprawia wydajność podczas odczytu pamięci.
Jednak, jak stwierdzono w innych odpowiedziach, czasami przestrzeń ma większe znaczenie niż sama wydajność. Być może przetwarzasz dużo danych na komputerze, który nie ma dużo pamięci RAM (można by użyć przestrzeni wymiany, ale jest DUŻO wolniejszy). Możesz ustawiać zmienne w programie, dopóki nie zostanie wykonane najmniejsze wypełnienie (co zostało to bardzo dobrze zilustrowane w niektórych innych odpowiedziach), ale jeśli to nie wystarczy, możesz jawnie wyłączyć wypełnianie, co jest pakowaniem .
źródło
Wypełnienie struktury eliminuje wyściółkę struktury, wypełnienie stosowane, gdy najważniejsze jest wyrównanie, pakowanie stosowane, gdy liczy się przestrzeń.
Niektóre kompilatory zapewniają
#pragma
tłumienie wypełniania lub spakowanie go do liczby bajtów. Niektóre zawierają w tym celu słowa kluczowe. Zasadniczo pragma, która służy do modyfikowania wypełnienia struktury, będzie miała następujący format (zależy od kompilatora):Na przykład ARM zapewnia
__packed
słowo kluczowe w celu powstrzymania wypełnienia struktury. Przejrzyj instrukcję kompilatora, aby dowiedzieć się więcej na ten temat.Tak więc upakowana struktura jest strukturą bez wypełnienia.
Zostaną wykorzystane ogólnie upakowane struktury
aby zaoszczędzić miejsce
sformatować strukturę danych do transmisji przez sieć przy użyciu jakiegoś protokołu (oczywiście nie jest to dobra praktyka, ponieważ musisz
poradzić sobie z endianowością)
źródło
Wypełnienie i pakowanie to tylko dwa aspekty tego samego:
Przy
mystruct_A
założeniu domyślnego wyrównania 4, każdy element jest wyrównany do wielokrotności 4 bajtów. Ponieważ rozmiarchar
wynosi 1, dopełnienie dlaa
ic
wynosi 4-1 = 3 bajty, podczas gdy wypełnienie nie jest wymagane, dlaint b
którego jest już 4 bajty. Działa w ten sam sposóbmystruct_B
.źródło
Pakowanie struktury odbywa się tylko wtedy, gdy wyraźnie powiesz swojemu kompilatorowi, aby spakował strukturę. Padding to, co widzisz. Twój 32-bitowy system dopełnia wyrównanie każdego pola do słowa. Gdybyś powiedział kompilatorowi, aby spakował struktury, miałyby one odpowiednio 6 i 5 bajtów. Nie rób tego jednak. Nie jest przenośny i powoduje, że kompilatory generują znacznie wolniejszy (a czasem nawet błędny) kod.
źródło
Nie ma w tym nic złego! Kto chce zrozumieć temat, musi wykonać następujące czynności,
źródło
Zasady wypełniania:
Dlaczego reguła 2: Zastanów się nad następującą strukturą,
Jeśli mielibyśmy stworzyć tablicę (2 struktur) tej struktury, na końcu nie będzie wymagane wypełnianie:
Dlatego rozmiar struct = 8 bajtów
Załóżmy, że mieliśmy stworzyć inną strukturę, jak poniżej:
Gdybyśmy utworzyli tablicę tej struktury, istnieją 2 możliwości liczby bajtów wypełnienia wymaganych na końcu.
A. Jeśli dodamy 3 bajty na końcu i wyrównamy je dla int, a nie Long:
B. Jeśli dodamy 7 bajtów na końcu i wyrównajmy do Długiej:
Adres początkowy drugiej tablicy jest wielokrotnością liczby 8 (tj. 24). Rozmiar struktury = 24 bajty
Dlatego poprzez wyrównanie adresu początkowego następnej tablicy struktury do wielokrotności największego elementu (tj. Jeśli mielibyśmy utworzyć tablicę tej struktury, pierwszy adres drugiej tablicy musi zaczynać się od adresu, który jest wielokrotnością największego członka struktury. Oto 24 (3 * 8)), możemy obliczyć liczbę bajtów dopełniania wymaganych na końcu.
źródło
Wyrównanie struktury danych to sposób, w jaki dane są rozmieszczane i dostępne w pamięci komputera. Składa się z dwóch oddzielnych, ale powiązanych zagadnień: wyrównania danych i wypełnienia struktury danych . Gdy współczesny komputer odczytuje lub zapisuje adres pamięci, zrobi to w kawałkach wielkości słowa (np. 4 bajty w systemie 32-bitowym) lub większych. Wyrównanie danych oznacza umieszczenie danych pod adresem pamięci równym pewnej wielokrotności wielkości słowa, co zwiększa wydajność systemu ze względu na sposób, w jaki procesor obsługuje pamięć. Aby wyrównać dane, może być konieczne wstawienie niektórych bezsensownych bajtów między końcem ostatniej struktury danych a początkiem następnej, czyli wypełnianiem struktury danych.
źródło