Dlaczego sizeof dla struktury nie jest równy sumie sizeof każdego elementu?

Dlaczego operator sizeof zwraca rozmiar większy dla struktury niż całkowite rozmiary jej elementów?

Author: tilz0R, 2008-09-23

12 answers

Jest to spowodowane wypełnieniem dodanym w celu spełnienia ograniczeń wyrównania. wyrównanie struktury danych wpływa zarówno na wydajność, jak i poprawność programów:

  • nieprawidłowy dostęp może być trudnym błędem (często SIGBUS).
  • nieprawidłowy dostęp może być miękkim błędem.
    • albo poprawione w sprzęcie, dla skromnej degradacji wydajności.
    • lub poprawione przez emulację w oprogramowaniu, w celu znacznego pogorszenia wydajności.
    • ponadto atomiczność i inne gwarancje współbieżności mogą zostać złamane, co prowadzi do subtelnych błędów.
Oto przykład użycia typowych ustawień dla procesora x86 (wszystkie używane tryby 32 i 64 bitowe):
struct X
{
    short s; /* 2 bytes */
             /* 2 padding bytes */
    int   i; /* 4 bytes */
    char  c; /* 1 byte */
             /* 3 padding bytes */
};

struct Y
{
    int   i; /* 4 bytes */
    char  c; /* 1 byte */
             /* 1 padding byte */
    short s; /* 2 bytes */
};

struct Z
{
    int   i; /* 4 bytes */
    short s; /* 2 bytes */
    char  c; /* 1 byte */
             /* 1 padding byte */
};

const int sizeX = sizeof(struct X); /* = 12 */
const int sizeY = sizeof(struct Y); /* = 8 */
const int sizeZ = sizeof(struct Z); /* = 8 */

Można zminimalizować rozmiar struktur poprzez sortowanie prętów według wyrównania (sortowanie według rozmiaru wystarcza do tego w typach podstawowych) (jak struktura Z w powyższym przykładzie).

WAŻNA UWAGA: zarówno standardy C, jak i C++ stwierdzają, że wyrównanie struktury jest zdefiniowane w implementacji. W związku z tym każdy kompilator może wybrać inne wyrównanie danych, co skutkuje różnymi i niekompatybilnymi układami danych. Z tego powodu, gdy mamy do czynienia z bibliotekami, które będą używane przez różne Kompilatory, ważne jest, aby zrozumieć, w jaki sposób Kompilatory wyrównują dane. Niektóre Kompilatory mają ustawienia wiersza poleceń i / lub specjalne polecenia #pragma do zmiany ustawień wyrównania struktury.

 690
Author: Kevin,
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
2017-07-05 20:25:02

Pakowanie i wyrównanie bajtów, jak opisano w FAQ tutaj:

/ Align = "left" / Wiele procesorów nie ma dostępu do 2-i 4-bajtowych ilości (np. ints i long ints), jeśli są zapchane w w każdą stronę.

Załóżmy, że masz taką strukturę:

struct {
    char a[3];
    short int b;
    long int c;
    char d[3];
};
/ Align = "left" / struktura w pamięci Tak:
+-------+-------+-------+-------+
|           a           |   b   |
+-------+-------+-------+-------+
|   b   |           c           |
+-------+-------+-------+-------+
|   c   |           d           |
+-------+-------+-------+-------+

Ale jest o wiele łatwiejsze dla procesora, jeśli kompilator organizuje to tak:

+-------+-------+-------+
|           a           |
+-------+-------+-------+
|       b       |
+-------+-------+-------+-------+
|               c               |
+-------+-------+-------+-------+
|           d           |
+-------+-------+-------+

W wersji spakowanej, zauważ, że jest to co najmniej trochę trudne dla ty i ja, żeby zobaczyć, jak owijają się pola b I c? W skrócie, dla procesora też jest ciężko. Dlatego większość kompilatorów będzie padać struktura (jakby z dodatkowymi, niewidzialnymi polami) jak ta:

+-------+-------+-------+-------+
|           a           | pad1  |
+-------+-------+-------+-------+
|       b       |     pad2      |
+-------+-------+-------+-------+
|               c               |
+-------+-------+-------+-------+
|           d           | pad3  |
+-------+-------+-------+-------+
 207
Author: EmmEff,
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
2015-11-30 23:33:40

Jeśli chcesz, aby struktura miała określony rozmiar z GCC na przykład użyj __attribute__((packed)).

W systemie Windows można ustawić wyrównanie do jednego bajtu podczas korzystania z cl.exe compier z opcją /ZP .

Zwykle łatwiej jest CPU uzyskać dostęp do danych, które są wielokrotnością 4 (lub 8), w zależności od platformy, a także kompilatora.

Więc jest to kwestia wyrównania w zasadzie.

Musisz mieć dobre powody, by to zmienić.

 30
Author: INS,
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
2013-08-29 15:26:46

Może to być spowodowane wyrównaniem bajtów i wypełnieniem tak, że struktura wychodzi na parzystą liczbę bajtów (lub słów) na platformie. Na przykład w C na Linuksie, następujące 3 struktury:

#include "stdio.h"


struct oneInt {
  int x;
};

struct twoInts {
  int x;
  int y;
};

struct someBits {
  int x:2;
  int y:6;
};


int main (int argc, char** argv) {
  printf("oneInt=%zu\n",sizeof(struct oneInt));
  printf("twoInts=%zu\n",sizeof(struct twoInts));
  printf("someBits=%zu\n",sizeof(struct someBits));
  return 0;
}

Mają członkowie, których rozmiary (w bajtach) wynoszą odpowiednio 4 bajty (32 bity), 8 bajtów (2x 32 bity) i 1 bajt (2+6 bitów). Powyższy program (na Linuksie za pomocą gcc) drukuje rozmiary jako 4, 8 i 4 - gdzie ostatnia struktura jest wyściełana tak, że jest to pojedyncze słowo (4 x 8 bitów bajtów na moim 32-bitowym Platforma).

oneInt=4
twoInts=8
someBits=4
 16
Author: Kyle Burton,
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
2014-01-09 12:53:22

Zobacz też:

Dla Microsoft Visual C:

Http://msdn.microsoft.com/en-us/library/2e70t5y1%28v=vs.80%29.aspx

I GCC twierdzą kompatybilność z kompilatorem Microsoftu.:

Http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html

Oprócz poprzednich odpowiedzi, należy pamiętać, że niezależnie od opakowania, nie ma members-order-guarantee w C++. Kompilatory mogą (i na pewno) dodawać wirtualną tabelę elementy struktury wskaźnika i podstawy do struktury. Nawet istnienie wirtualnej tabeli nie jest zapewnione przez standard (implementacja Wirtualnego mechanizmu nie jest określona) i dlatego można wnioskować, że taka gwarancja jest po prostu niemożliwa.

Jestem pewien, że member-order jest gwarantowane w C, ale nie liczyłbym na to, pisząc program wieloplatformowy lub kompilator.

 12
Author: lkanab,
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
2017-04-24 21:45:36

Rozmiar struktury jest większy niż suma jej części ze względu na to, co nazywa się pakowaniem. Konkretny procesor ma preferowany rozmiar danych, z którym współpracuje. Większość nowoczesnych procesorów preferowany rozmiar, jeśli 32-bity (4 bajty). Dostęp do pamięci, gdy dane znajdują się na tej granicy, jest bardziej wydajny niż rzeczy, które przecinają tę granicę rozmiaru.

Na przykład. Rozważmy prostą strukturę:
struct myStruct
{
   int a;
   char b;
   int c;
} data;

Jeśli maszyna jest maszyną 32-bitową i dane są wyrównane na 32-bitową granicę, widzimy natychmiastowy problem (zakładając brak wyrównania struktury). W tym przykładzie załóżmy, że dane struktury zaczynają się od adresu 1024 (0x400-zauważ, że najniższe 2 bity są zerem, więc dane są wyrównane do granicy 32-bitowej). Dostęp do danych.a będzie działać dobrze, ponieważ zaczyna się na granicy-0x400. Dostęp do danych.b również będzie działać dobrze, ponieważ znajduje się pod adresem 0x404 - kolejna 32-bitowa granica. Ale niezalegowana struktura umieściłaby dane.c pod adresem 0x405. Na 4 bajty danych.c są w 0x405, 0x406, 0x407, 0x408. Na 32-bitowej maszynie system odczytywał dane.c podczas jednego cyklu pamięci, ale otrzyma tylko 3 z 4 bajtów (czwarty bajt jest na następnej granicy). Tak więc system musiałby zrobić drugi dostęp do pamięci, aby uzyskać 4. bajt,

Teraz, jeśli zamiast umieszczać dane.c pod adresem 0x405 kompilator wypełnił strukturę o 3 bajty i umieścił dane.c pod adresem 0x408, wtedy system potrzebuje tylko 1 cyklu, aby odczytać dane, odcinając dostęp czas do tego elementu danych o 50%. Wyściółka zmienia wydajność pamięci na wydajność przetwarzania. Biorąc pod uwagę, że komputery mogą mieć ogromne ilości pamięci (wiele gigabajtów), Kompilatory uważają, że swap (szybkość nad rozmiarem) jest rozsądny.

Niestety, ten problem staje się zabójczy, gdy próbujesz wysłać struktury przez sieć lub nawet zapisać dane binarne do pliku binarnego. Padding wstawiony pomiędzy elementy struktury lub klasy może zakłócić dane przesyłane do pliku lub sieć. Aby napisać kod przenośny (taki, który trafi do kilku różnych kompilatorów), prawdopodobnie będziesz musiał uzyskać dostęp do każdego elementu struktury osobno, aby zapewnić odpowiednie "pakowanie".

Z drugiej strony, różne Kompilatory mają różne możliwości zarządzania pakowaniem struktury danych. Na przykład w Visual C/C++ kompilator obsługuje polecenie # pragma pack. Pozwoli to dostosować pakowanie i wyrównywanie danych.

Na przykład:

#pragma pack 1
struct MyStruct
{
    int a;
    char b;
    int c;
    short d;
} myData;

I = sizeof(myData);

I should teraz mają długość 11. Bez pragma mógłbym być dowolny od 11 do 14 (a dla niektórych systemów aż 32), w zależności od domyślnego pakowania kompilatora.

 9
Author: sid1138,
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
2015-06-12 06:50:22

Może to zrobić, jeśli w sposób niejawny lub jawny ustawisz wyrównanie struktury. Struktura, która jest wyrównana 4, zawsze będzie wielokrotnością 4 bajtów, nawet jeśli rozmiar jej członków byłby czymś, co nie jest wielokrotnością 4 bajtów.

Również biblioteka może być skompilowana pod x86 z 32-bitowymi ints i możesz porównywać jej składniki na 64-bitowym procesie, dałoby ci inny wynik, gdybyś robił to ręcznie.

 6
Author: Orion Adrian,
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
2008-09-23 04:27:07

C99 n1256 standard draft

Http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf

6.5.3.4 operator sizeof :

3 w przypadku zastosowania do operandu o strukturze lub typie Unii, wynikiem jest całkowita liczba bajtów w takim obiekcie, w tym wewnętrzne i końcowe wyściółki.

6.7.2.1 struktura i specyfikacja związku :

13 ... Może być bez nazwy wyściółka wewnątrz konstrukcji obiekt, ale nie na początku.

I:

15 na końcu struktury lub Unii może występować nienazwana wyściółka.

Nowa funkcja C99 flexible array member (struct S {int is[];};) może również wpływać na padding:

16 jako szczególny przypadek, ostatni element struktury z więcej niż jednym członem może mają niekompletny typ tablicy; nazywa się to elastycznym elementem tablicy. W większości sytuacji, elastyczny element tablicy jest ignorowany. W szczególności rozmiar struktury jest tak, jakby elastyczny element tablicy został pominięty, z wyjątkiem tego, że może mieć więcej padding niż to przeoczenie sugerowałoby.

Załącznik J zagadnienia przenośności powtarza:

Następujące są nieokreślone: ...

    Wartość bajtów wypełnienia podczas przechowywania wartości w strukturach lub związkach (6.2.6.1)

C++11 n3337 standard projekt

Http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf

5.3.3 Rozmiar :

2 Po zastosowaniu do klasy, wynikiem jest liczba bajtów w obiekcie tej klasy łącznie z dowolnym wypełnieniem wymaganym dla umieszczanie obiektów tego typu w tablicy.

9.2 class members :

Wskaźnik do obiektu struktury o standardowym układzie, odpowiednio przekonwertowany za pomocą reinterpret_cast, wskazuje na jego początkowe member (lub jeśli member jest polem bitowym, to do jednostki, w której się znajduje) i odwrotnie. [Uwaga: Z tego powodu wewnątrz obiektu struktury standard-layout może występować nienazwane wypełnienie, ale nie na jego początku, jeżeli jest to konieczne do osiągnięcia odpowiedniego dostosowania. - Uwaga końcowa]

Znam tylko tyle C++, żeby zrozumieć notkę : -)

 6
Author: Ciro Santilli TRUMP BAN IS BAD,
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
2020-06-20 09:12:55

Oprócz innych odpowiedzi, struktura może (ale zazwyczaj nie ma) posiadać funkcje wirtualne, w takim przypadku rozmiar struktury będzie również zawierał przestrzeń dla vtbl.

 4
Author: JohnMcG,
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
2008-09-23 13:38:11

Język C pozostawia kompilatorowi pewną swobodę co do położenia elementów strukturalnych w pamięci:

  • otwory pamięci mogą pojawić się pomiędzy dowolnymi dwoma komponentami i po ostatnim komponencie. Było to spowodowane faktem, że niektóre typy obiektów na komputerze docelowym mogą być ograniczone granicami adresowania
  • rozmiar"otworów pamięci" zawarty w wyniku operatora sizeof. Rozmiar nie obejmuje tylko rozmiaru tablicy elastycznej, która jest dostępna w C / C++
  • niektóre implementacje języka pozwalają kontrolować układ pamięci struktur za pomocą opcji pragma i kompilatora

Język C zapewnia programiście pewną pewność co do układu elementów w strukturze:

  • Kompilatory wymagane do przypisania sekwencji komponentów zwiększających adresy pamięci
  • adres pierwszego komponentu pokrywa się z adresem początkowym struktury
  • nienazwane pola bitowe mogą być zawarte w strukturze do wymaganego adresu wyrównania sąsiednich elementów

Problemy związane z wyrównaniem elementów:

  • różne komputery ustawiają krawędzie obiektów na różne sposoby
  • różne ograniczenia szerokości pola bitowego
  • Komputery różnią się sposobem przechowywania bajtów w słowie (Intel 80x86 i Motorola 68000)

Jak działa wyrównanie:

  • objętość zajmowana przez strukturę jest obliczana jako wielkość wyrównanego pojedynczego elementu tablicy takich struktur. Struktura powinna kończy się tak, aby pierwszy element następnej struktury nie spełniał wymogów wyrównania

P. S bardziej szczegółowe informacje są dostępne tutaj: "Samuel P. Harbison, Guy L. Steele C a Reference, (5.6.2 - 5.6.7)"

 4
Author: bruziuz,
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
2015-07-29 06:38:59

Chodzi o to, że dla szybkości i pamięci podręcznej, operandy powinny być odczytywane z adresów dopasowanych do ich naturalnej wielkości. Aby tak się stało, elementy struktury klocków kompilatora zostaną wyrównane tak, aby następujący element lub następująca struktura były wyrównane.

struct pixel {
    unsigned char red;   // 0
    unsigned char green; // 1
    unsigned int alpha;  // 4 (gotta skip to an aligned offset)
    unsigned char blue;  // 8 (then skip 9 10 11)
};

// next offset: 12

Architektura x86 zawsze była w stanie pobrać niepoprawne adresy. Jest to jednak wolniejsze i gdy niewspółosiowość nakłada się na dwie różne linie pamięci podręcznej, to eksmituje dwie linie pamięci podręcznej, gdy wyrównany dostęp tylko eksmituje jeden.

Niektóre architektury rzeczywiście muszą pułapki na nieprawidłowo ustawione odczyty i zapisy, oraz wczesne wersje architektury ARM (ta, która ewoluowała do wszystkich dzisiejszych procesorów mobilnych) ... właściwie to zwrócili złe dane na ich temat. (Zignorowali bity niskiego rzędu.)

Na koniec zauważ, że linie pamięci podręcznej mogą być dowolnie duże, a kompilator nie próbuje ich odgadnąć ani dokonać kompromisu między spacją a prędkością. Zamiast tego decyzje w sprawie dostosowania są częścią ABI i reprezentuje Minimalne wyrównanie, które ostatecznie równomiernie wypełni linię bufora.

TL; DR: wyrównanie jest ważne.

 3
Author: DigitalRoss,
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-02-24 06:46:13

Wśród innych dobrze wyjaśnionych odpowiedzi na temat wyrównywania pamięci i wypełniania / pakowania struktury, jest coś, co odkryłem w samym pytaniu, czytając je uważnie.

"dlaczego sizeof dla struktury nie jest równa sumie sizeof każdego członka?"

"dlaczego operator sizeof zwraca rozmiar większy dla struktury niż całkowite rozmiary elementów struktury"?

Oba pytania sugerują coś, co jest źle. Przynajmniej w ogólnym, nie przykładowym widoku skupionym, co ma miejsce w tym przypadku.

Wynik operandu sizeof zastosowanego do obiektu struktury może być równy sumie sizeof zastosowanej do każdego elementu osobno. To nie musi być większe/inne.

Jeśli nie ma powodu do wyściełania, Żadna pamięć nie będzie wyściełana.


Jedna z najbardziej implementacji, jeśli struktura zawiera tylko elementy tego samego typu:

struct foo {
   int a;   
   int b;
   int c;     
} bar;

Zakładając sizeof(int) == 4, Rozmiar struktury bar będzie równy sumie rozmiarów wszystkich członów razem, sizeof(bar) == 12. Nie ma tu żadnej wyściółki.

To samo dotyczy na przykład tutaj:

struct foo {
   short int a;   
   short int b;
   int c;     
} bar;

Zakładając sizeof(short int) == 2 i sizeof(int) == 4. Suma przydzielonych bajtów dla a i b jest równa przydzielonym bajtom dla c, największego elementu i dzięki temu wszystko jest idealnie wyrównane. Tak więc sizeof(bar) == 8.

Jest to również przedmiot drugiego najpopularniejszego pytania dotyczącego wyściółki struktury, tutaj:

 0
Author: RobertS supports Monica Cellio,
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
2020-06-29 14:41:39