Wyściółka struktury i pakowanie

Rozważmy:

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 odbywa się wyściółka lub pakowanie?

Author: gsamaras, 2010-11-29

7 answers

Padding wyrównuje elementy struktury do "naturalnych" granic adresów - powiedzmy, że elementy int będą miały offsety, które są mod(4) == 0 na platformie 32-bitowej. Padding jest domyślnie włączony. Wstawia następujące "luki"do pierwszej struktury:

struct mystruct_A {
    char a;
    char gap_0[3]; /* inserted by compiler: for alignment of b */
    int b;
    char c;
    char gap_1[3]; /* -"-: for alignment of the whole struct in an array */
} x;

Pakowanie , z drugiej strony uniemożliwia kompilatorowi wykonywanie wypełniania-musi to być wyraźnie wymagane-w GCC jest to __attribute__((__packed__)), więc:

struct __attribute__((__packed__)) mystruct_A {
    char a;
    int b;
    char c;
};

Wytworzy strukturę o rozmiarze 6 na 32-bitowym Architektura.

Uwaga - niepodpisany dostęp do pamięci jest wolniejszy na architekturach, które na to pozwalają (takich jak x86 i amd64) i jest wyraźnie zabroniony na architekturach o ścisłym wyrównaniu, takich jak SPARC.

 211
Author: Nikolai Fetissov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-29 18:14:14

Wiem, że to pytanie jest stare i większość odpowiedzi tutaj wyjaśnia padding naprawdę dobrze, ale próbując je zrozumieć sam pomyślałem, że posiadanie "wizualnego" obrazu tego, co się dzieje pomogło.

Procesor odczytuje pamięć w "kawałkach" o określonym rozmiarze (word). Powiedzmy, że słowo procesora ma długość 8 bajtów. Pamięć będzie postrzegana jako duży wiersz składający się z 8 bajtowych bloków. Za każdym razem, gdy musi uzyskać jakieś informacje z pamięci, dotrze do jednego z tych bloków i dostanie to.

Wyrównanie Zmiennych

Jak widać na powyższym obrazku, nie ma znaczenia, gdzie znajduje się znak (1 bajtowy), ponieważ będzie on wewnątrz jednego z tych bloków, wymagając od procesora przetwarzania tylko 1 słowa.

Gdy mamy do czynienia z danymi większymi niż jeden bajt, takimi jak 4-bajtowy int lub 8-bajtowy double, sposób ich wyrównania w pamięci ma znaczenie, ile słów będzie musiało być przetwarzanych przez procesor. Jeśli 4-bajtowe kawałki są wyrównane w taki sposób, że zawsze pasują do wnętrza bloku (adres pamięci jest wielokrotność 4) tylko jedno słowo będzie musiało zostać przetworzone. W przeciwnym razie fragment 4-bajtów może mieć część siebie na jednym bloku, a część na drugim, wymagając od procesora przetworzenia 2 słów w celu odczytania tych danych.

To samo dotyczy 8-bajtowego dubletu, z tym, że teraz musi on znajdować się w adresie pamięci wielokrotności 8, aby zagwarantować, że zawsze będzie w bloku.

Dotyczy to 8-bajtowego edytora tekstu, ale pojęcie to odnosi się do innych rozmiarów słów.

Wyściółka działa przez wypełnianie luk między tymi danymi, aby upewnić się, że są one wyrównane z tymi blokami, poprawiając w ten sposób wydajność podczas odczytu pamięci.

Jednak, jak stwierdzono na innych odpowiedziach, czasami przestrzeń ma znaczenie bardziej niż sam performance. Być może przetwarzasz wiele danych na komputerze, który nie ma dużo pamięci RAM (przestrzeń wymiany może być używana, ale jest znacznie wolniejsza). Można było układać zmienne w programie do momentu, aż zrobi się najmniej wypełnienia (jak to było bardzo widoczne w niektórych inne odpowiedzi), ale jeśli to nie wystarczy, możesz wyraźnie wyłączyć padding, czyli to, czym jest pakowanie.

 28
Author: IanC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-08 00:25:57

Pakowanie struktury tłumi wyściółkę struktury, wyściółkę używaną, gdy wyrównanie ma największe znaczenie, pakowanie używane, gdy liczy się przestrzeń.

Niektóre Kompilatory zapewniają #pragma, aby powstrzymać padding lub upakować go do n liczby bajtów. Niektóre zapewniają słowa kluczowe, aby to zrobić. Ogólnie pragma, która jest używana do modyfikowania wypełnienia struktury, będzie w poniższym formacie (zależy od kompilatora):

#pragma pack(n)

Na przykład ARM dostarcza słowa kluczowego __packed do tłumienia wypełnienia struktury. Przejdź przez swój Instrukcja kompilatora, aby dowiedzieć się więcej na ten temat.

Tak więc spakowana struktura jest strukturą bez wyściółki.

Ogólnie używane będą struktury spakowane

  • Aby zaoszczędzić miejsce

  • Sformatować strukturę danych do przesyłania przez sieć przy użyciu niektórych protokół (nie jest to oczywiście dobra praktyka, ponieważ musisz
    deal with endianness)

 20
Author: user2083050,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-05 11:23:18

(powyższe odpowiedzi wyjaśniły powód dość jasno, ale wydaje się nie do końca jasne, co do wielkości wyściółki, więc dodam odpowiedź zgodnie z tym, czego uczę się z The Lost Art of C structure Packing)


Memory align rules - for struct

Zasady:

  • przed każdym pojedynczym członem będzie padding tak, aby zaczynał się od adresu podzielnego przez jego rozmiar. np. w systemie 64 bitowym, int powinien zacznij od adresu podzielnego przez 4 i długiego przez 8, krótkiego przez 2.
  • char i char [] jest specjalny, może być dowolnym adresem pamięci, więc nie potrzebują wypełnienia przed nimi.
  • dla struct, poza potrzebą wyrównania dla każdego pojedynczego pręta, rozmiar całej struktury zostanie wyrównany do rozmiaru podzielnego przez rozmiar największego pojedynczego pręta, przez wypełnienie na końcu. np. jeśli największy element struct jest długi, to jest podzielny przez 8, int przez 4, short then przez 2.

Kolejność członków:

  • kolejność elementów może mieć wpływ na rzeczywistą wielkość struktury, więc miej to na uwadze. np. stu_c i stu_d z powyższego przykładu mają te same pręty, ale w innej kolejności, co skutkuje różną wielkością struktury.

Adres w pamięci - dla struct

Zasady:

  • 32 bitowy system
    Rozpoczyna się od (n * 8) bajtów.
    Reason : największy osobnik STRUCT member to 8 bajtów.
  • 64 bitowy system
    Rozpoczyna się od (n * 16) bajtów.
    Reason: największy pojedynczy element struktury ma 16 bajtów.

Puste miejsce :

  • pusta przestrzeń między strukturami może być używana przez zmienne nietruktowe, które mogłyby się zmieścić.

Przykład

(dla systemu 64 bitowego)

Memory_align.c:

/**
 * Memory align & padding - for struct.
 * compile: gcc memory_align.c
 * execute: ./a.out
 */ 
#include <stdio.h>

// size is 8, 4 + 1, then round to multiple of 4 (int's size),
struct stu_a {
    int i;
    char c;
};

// size is 16, 8 + 1, then round to multiple of 8 (long's size),
struct stu_b {
    long l;
    char c;
};

// size is 24, l need padding by 4 before it, then round to multiple of 8 (long's size),
struct stu_c {
    int i;
    long l;
    char c;
};

// size is 16, 8 + 4 + 1, then round to multiple of 8 (long's size),
struct stu_d {
    long l;
    int i;
    char c;
};

// size is 16, 8 + 4 + 1, then round to multiple of 8 (double's size),
struct stu_e {
    double d;
    int i;
    char c;
};

// size is 24, d need align to 8, then round to multiple of 8 (double's size),
struct stu_f {
    int i;
    double d;
    char c;
};

// size is 4,
struct stu_g {
    int i;
};

// size is 8,
struct stu_h {
    long l;
};

// test - padding within a single struct,
int test_struct_padding() {
    printf("%s: %ld\n", "stu_a", sizeof(struct stu_a));
    printf("%s: %ld\n", "stu_b", sizeof(struct stu_b));
    printf("%s: %ld\n", "stu_c", sizeof(struct stu_c));
    printf("%s: %ld\n", "stu_d", sizeof(struct stu_d));
    printf("%s: %ld\n", "stu_e", sizeof(struct stu_e));
    printf("%s: %ld\n", "stu_f", sizeof(struct stu_f));

    printf("%s: %ld\n", "stu_g", sizeof(struct stu_g));
    printf("%s: %ld\n", "stu_h", sizeof(struct stu_h));

    return 0;
}

// test - address of struct,
int test_struct_address() {
    printf("%s: %ld\n", "stu_g", sizeof(struct stu_g));
    printf("%s: %ld\n", "stu_h", sizeof(struct stu_h));
    printf("%s: %ld\n", "stu_f", sizeof(struct stu_f));

    struct stu_g g;
    struct stu_h h;
    struct stu_f f1;
    struct stu_f f2;
    int x = 1;
    long y = 1;

    printf("address of %s: %p\n", "g", &g);
    printf("address of %s: %p\n", "h", &h);
    printf("address of %s: %p\n", "f1", &f1);
    printf("address of %s: %p\n", "f2", &f2);
    printf("address of %s: %p\n", "x", &x);
    printf("address of %s: %p\n", "y", &y);

    // g is only 4 bytes itself, but distance to next struct is 16 bytes(on 64 bit system) or 8 bytes(on 32 bit system),
    printf("space between %s and %s: %ld\n", "g", "h", (long)(&h) - (long)(&g));

    // h is only 8 bytes itself, but distance to next struct is 16 bytes(on 64 bit system) or 8 bytes(on 32 bit system),
    printf("space between %s and %s: %ld\n", "h", "f1", (long)(&f1) - (long)(&h));

    // f1 is only 24 bytes itself, but distance to next struct is 32 bytes(on 64 bit system) or 24 bytes(on 32 bit system),
    printf("space between %s and %s: %ld\n", "f1", "f2", (long)(&f2) - (long)(&f1));

    // x is not a struct, and it reuse those empty space between struts, which exists due to padding, e.g between g & h,
    printf("space between %s and %s: %ld\n", "x", "f2", (long)(&x) - (long)(&f2));
    printf("space between %s and %s: %ld\n", "g", "x", (long)(&x) - (long)(&g));

    // y is not a struct, and it reuse those empty space between struts, which exists due to padding, e.g between h & f1,
    printf("space between %s and %s: %ld\n", "x", "y", (long)(&y) - (long)(&x));
    printf("space between %s and %s: %ld\n", "g", "y", (long)(&y) - (long)(&h));

    return 0;
}

int main(int argc, char * argv[]) {
    test_struct_padding();
    // test_struct_address();

    return 0;
}

Wynik realizacji - test_struct_padding():

stu_a: 8
stu_b: 16
stu_c: 24
stu_d: 16
stu_e: 16
stu_f: 24
stu_g: 4
stu_h: 8

Wynik realizacji - test_struct_address():

stu_g: 4
stu_h: 8
stu_f: 24
address of g: 0x7fffd63a95d0  // struct variable - address dividable by 16,
address of h: 0x7fffd63a95e0  // struct variable - address dividable by 16,
address of f1: 0x7fffd63a95f0 // struct variable - address dividable by 16,
address of f2: 0x7fffd63a9610 // struct variable - address dividable by 16,
address of x: 0x7fffd63a95dc  // non-struct variable - resides within the empty space between struct variable g & h.
address of y: 0x7fffd63a95e8  // non-struct variable - resides within the empty space between struct variable h & f1.
space between g and h: 16
space between h and f1: 16
space between f1 and f2: 32
space between x and f2: -52
space between g and x: 12
space between x and y: 12
space between g and y: 8
 19
Author: Eric Wang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-12 15:58:10

Wyściółka i pakowanie to tylko dwa aspekty tego samego:

  • pakowanie lub wyrównanie to rozmiar, do którego każdy element jest zaokrąglony
  • padding jest dodatkową przestrzenią dodaną do wyrównania

W mystruct_A, przy założeniu domyślnego wyrównania 4, każdy element jest wyrównany na wielokrotności 4 bajtów. Ponieważ Rozmiar char wynosi 1, wypełnienie dla a i c wynosi 4 - 1 = 3 bajty, podczas gdy wypełnienie nie jest wymagane dla int b, które wynosi już 4 bajty. Działa tak samo sposób na mystruct_B.

 4
Author: casablanca,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-29 17:23:05

Pakowanie struktury odbywa się tylko wtedy, gdy kompilator wyraźnie każe spakować strukturę. Padding jest tym, co widzisz. Twój 32-bitowy system wypełnia każde pole do wyrównania słów. Gdybyś powiedział kompilatorowi, aby spakował struktury, byłyby to odpowiednio 6 i 5 bajtów. Nie rób tego. Nie jest przenośny i sprawia, że Kompilatory generują znacznie wolniejszy (a czasem nawet błędny) kod.

 1
Author: nmichaels,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-29 17:23:29

Wyrównanie struktury danych to sposób, w jaki dane są ułożone i dostępne w pamięci komputera. Składa się on z dwóch oddzielnych, ale powiązanych ze sobą zagadnień: wyrównania danych i wypełnienia struktury danych . Gdy nowoczesny komputer odczytuje lub zapisuje adres pamięci, zrobi to w kawałkach o wielkości słowa (np. 4 bajtowe kawałki w systemie 32-bitowym) lub większych. Wyrównanie danych oznacza umieszczenie danych pod adresem pamięci równym pewnej wielokrotności rozmiaru 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 wstawianie pewnych bezsensownych bajtów między końcem ostatniej struktury danych a początkiem następnej, czyli wypełnieniem struktury danych.

  1. w celu wyrównania danych w pamięci, jeden lub więcej pustych bajtów (adresów) są wstawiane (lub pozostawiane puste) pomiędzy adresami pamięci, które są przydzielane innym członkom struktury podczas alokacji pamięci. Pojęcie to nosi nazwę structure padding.
  2. Architektura a procesor komputerowy jest w taki sposób, że może odczytywać 1 słowo (4 bajty w 32-bitowym procesorze) z pamięci na raz.
  3. Aby skorzystać z tej przewagi procesora, dane są zawsze wyrównywane jako 4 bajtowy pakiet, co prowadzi do wstawiania pustych adresów między adresami innego członka.
  4. z powodu tej koncepcji wypełnienia struktury w C, rozmiar struktury zawsze nie jest taki sam, jak myślimy.
 -1
Author: manoj yadav,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-14 09:07:53