Hvorfor er ikke sizeof for en struct lik summen av sizeof av hvert medlem?

stemmer
554

Hvorfor 'sizeof' operatør returnere en størrelse større for en struktur enn den totale størrelsen på konstruksjonens medlemmer?

Publisert på 23/09/2008 klokken 04:24
kilden bruker
På andre språk...                            


11 svar

stemmer
545

Dette er på grunn av polstringen tilsatt for å tilfredsstille innrettings begrensninger. Datastruktur justering påvirker både ytelse og riktigheten av programmer:

  • Feilplassert tilgang kan være en vanskelig feil (ofte SIGBUS).
  • Mis-justert tilgang kan være en myk feil.
    • Enten rettet opp i maskinvare, for en beskjeden ytelse-degradering.
    • Eller korrigert av emulering i programvare, for en alvorlig prestasjonsforringelse.
    • I tillegg kan atomicity og andre samtidighet garantier brytes, fører til subtile feil.

Her er et eksempel for vanlige innstillinger for en x86 prosessor (brukte 32 og 64 bits modus):

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 */

Man kan minimalisere størrelsen av strukturer av sorterings medlemmer av innretting (sortering etter størrelse er tilstrekkelig for at det i hovedtyper) (lignende struktur Zi eksempelet ovenfor).

VIKTIG: Både C og C ++ standarder sier at strukturen justeringen er implementering-definert. Derfor er hver kompilatoren kan velge å justere data på en annen måte, noe som resulterer i forskjellige og inkompatible data-oppsett. Av denne grunn, når du arbeider med biblioteker som vil bli brukt av ulike kompilatorer, er det viktig å forstå hvordan kompilatorer justere data. Noen kompilatorer har kommandolinje innstillinger og / eller spesielle #pragmauttalelser for å endre strukturen justeringsinnstillinger.

Svarte 23/09/2008 kl. 04:25
kilden bruker

stemmer
146

Pakking og byte justering, som beskrevet i C FAQ her :

Det er for justering. Mange prosessorer kan ikke få tilgang til 2- og 4-byte mengder (f.eks ints og lange ints) hvis de er crammed i alle-som-måte.

Anta at du har denne strukturen:

struct {
    char a[3];
    short int b;
    long int c;
    char d[3];
};

Nå tenker du kanskje at det burde være mulig å pakke denne strukturen i minnet som dette:

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

Men det er mye, mye enklere på prosessoren hvis kompilatoren arrangerer det slik:

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

I pakket versjon, legge merke til hvordan det er i det minste litt vanskelig for deg og meg å se hvordan b og c felt vikle rundt? I et nøtteskall, er det vanskelig for prosessoren, også. Derfor er de fleste kompilatorer vil puten strukturen (som om med ekstra, usynlige felter) som dette:

+-------+-------+-------+-------+
|           a           | pad1  |
+-------+-------+-------+-------+
|       b       |     pad2      |
+-------+-------+-------+-------+
|               c               |
+-------+-------+-------+-------+
|           d           | pad3  |
+-------+-------+-------+-------+
Svarte 23/09/2008 kl. 04:27
kilden bruker

stemmer
23

Hvis du vil at strukturen for å ha en viss størrelse med GCC for eksempel bruk __attribute__((packed)).

I Windows kan du angi justeringen til en byte når du bruker cl.exe compier med / Zp alternativet .

Vanligvis er det lettere for CPU for å få tilgang til data som er et multiplum av 4 (eller 8), avhengig av plattformen og også på kompilatoren.

Så det er et spørsmål om justering i utgangspunktet.

Du må ha gode grunner til å endre det.

Svarte 23/09/2008 kl. 07:06
kilden bruker

stemmer
11

Dette kan være på grunn av byte justering og polstring slik at strukturen kommer ut til et likt antall byte (eller ord) på din plattform. For eksempel i C på Linux, følgende 3 strukturer:

#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;
}

Har medlemmer som er størrelser (i byte) er 4 bytes (32 bit), 8 byte (2 x 32 biter) og en byte (2 + 6 biter) hhv. Ovennevnte program (på Linux ved hjelp av gcc) skriver størrelsene som 4, 8 og 4 - hvor den siste strukturen er polstret slik at det er et enkelt ord (4 x 8 bits byte på min 32bit plattform).

oneInt=4
twoInts=8
someBits=4
Svarte 23/09/2008 kl. 04:31
kilden bruker

stemmer
9

Se også:

for Microsoft Visual C:

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

og GCC krav kompatibilitet med Microsofts kompilatoren .:

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

I tillegg til de tidligere svar, vær oppmerksom på at uansett emballasjen, er det ingen medlemmer på bestilling-garanti i C ++ . Kompilatorer kan (og sikkert gjør) add virtuelle tabellpekeren og basisstrukturer medlemmer til konstruksjonen. Selv om eksistensen av virtuelle bordet ikke er sikret av den standard (virtuelle mekanisme implementering er ikke spesifisert), og derfor kan man konkludere med at en slik garanti er bare umulig.

Jeg er ganske sikker på at medlem-order er garantert i C , men jeg ville ikke stole på det, når du skriver en cross-platform eller kryss-kompilator program.

Svarte 31/05/2011 kl. 09:27
kilden bruker

stemmer
6

Størrelsen av en struktur er større enn summen av delene på grunn av det som kalles pakking. En spesiell prosessor har en foretrukket data størrelse at den passer. De fleste moderne prosessorer foretrukne størrelse hvis 32-bits (4 B). Tilgang minnet når dataene er på denne typen grensen er mer effektiv enn ting som skreve denne størrelsen grensen.

For eksempel. Vurdere enkel struktur:

struct myStruct
{
   int a;
   char b;
   int c;
} data;

Hvis maskinen er en 32-bits maskin og data blir justert på en 32-bit grense, ser vi et umiddelbart problem (forutsatt ingen struktur justering). I dette eksempel, la oss anta at strukturen data starter på adressen 1024 (0x400 - merk at de laveste 2 bitene er null, slik at dataene blir justert til en 32-bit grense). Tilgangen til data.a vil fungere fint fordi det begynner på en grense - 0x400. Tilgangen til data.b vil også fungere fint, fordi det er på adressen 0x404 - en annen 32-bit grensen. Men en unaligned struktur ville sette data.c på adressen 0x405. De 4 byte av data.c er på 0x405, 0x406, 0x407, 0x408. På en 32-bits maskin, ville systemet lese data.c under en lagersyklusen, men ville bare få 3 av de 4 byte (den fjerde byte er på neste grense). Så, ville systemet ha å gjøre en andre minnetilgang for å få fjerde byte,

Hvis nå stedet for å sette data.c ved adresse 0x405, polstret kompilatoren strukturen av 3 bytes og sette data.c ved adresse 0x408, da systemet, vil bare trenge en syklus for å lese data, kutte aksesstid til den dataelement med 50%. Polstring bytter minne effektivitet for behandling effektivitet. Gitt at datamaskiner kan ha store mengder minne (mange gigabyte), kompilatorer føler at swap (fart over størrelse) er en rimelig en.

Dessverre blir dette problemet en morder når du prøver å sende strukturer over et nettverk eller skrive binære data til en binær fil. Som settes inn mellom elementer i en struktur eller klasse polstring kan forstyrre data som sendes til filen eller nettverket. For å skrive portabel kode (en som vil gå til flere forskjellige kompilatorer), vil du sannsynligvis nødt til å få tilgang til hvert element i konstruksjonen separat for å sikre riktig "pakking".

På den annen side, ulike kompilatorer har ulike evner til å administrere datastruktur pakking. For eksempel, i Visual C / C ++ kompilatoren støtter #pragma pakken kommandoen. Dette vil tillate deg å justere data pakking og justering.

For eksempel:

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

I = sizeof(myData);

Jeg skal nå ha lengden på 11. Uten pragma, jeg kan være alt fra 11 til 14 (og for noen systemer, så mye som 32), avhengig av standard pakking av kompilatoren.

Svarte 10/06/2015 kl. 15:07
kilden bruker

stemmer
5

C99 N1256 standard utkast

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

6.5.3.4 Den sizeof operatør :

3. Når det anvendes på en operand som har strukturen eller union typen, er resultatet det totale antall byter i et slikt objekt, herunder indre og bakre polstring.

6.7.2.1 Struktur og fagforeningsbransjen :

13 ... Det kan være navngitte padding innenfor en struktur objekt, men ikke ved dens begynnelse.

og:

15 Det kan være navngitt polstring ved enden av en struktur eller union.

Den nye C99 fleksible matrise medlem funksjonen ( struct S {int is[];};) også kan påvirke padding:

16 Som et spesielt tilfelle, kan den siste del av en struktur med mer enn ett medlem heter har en ufullstendig matrise type; dette kalles en fleksibel matrise medlem. I de fleste situasjoner er det fleksible utvalg medlem ignorert. Spesielt er størrelsen på strukturen som om det fleksible matrisen medlem ble sløyfet med unntagelse for at den kan ha flere etterfølgende polstring enn utelatelsen skulle tilsi.

Vedlegg J Portabilitet Issues gjentar:

Følgende er uspesifisert: ...

  • Verdien av stopp byte ved lagring av verdier i strukturer eller tilslutninger (6.2.6.1)

C ++ 11 N3337 standard utkast

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

5.3.3 sizeof :

2 Når det anvendes på en klasse, er resultatet antall byte i et objekt av klassen inkludert enhver pute som kreves for å plassere objekter av denne typen i en matrise.

9.2 Klasse medlemmer :

En peker til en standard-layout struct gjenstand, hvilke hensiktsmessig omdannes ved hjelp av en reinterpret_cast, peker til sin utgangselement (eller hvis det medlem er en bit-felt, og deretter til den enhet i hvilken det befinner seg) og vice versa. [Merk: Det kan derfor være anonym padding innenfor en standard-layout struct objekt, men ikke i begynnelsen, som er nødvendig for å oppnå riktig justering. - end note]

Jeg bare vet nok C ++ til å forstå notatet :-)

Svarte 04/05/2016 kl. 15:38
kilden bruker

stemmer
5

Det kan gjøre det hvis du implisitt eller eksplisitt har satt justeringen av struct. En struct som er på linje 4 vil alltid være et multiplum av 4 byte, selv om størrelsen på sine medlemmer ville være noe som ikke er et multiplum av 4 byte.

Også et bibliotek kan bli satt sammen under x86 med 32-bits ints og man kan sammenlikne dets komponenter på en 64-bit prosessen ville vil gi et forskjellig resultat hvis man gjorde dette for hånd.

Svarte 23/09/2008 kl. 04:27
kilden bruker

stemmer
4

I tillegg til de andre svar, en struct kan (men vanligvis ikke) ha virtuelle funksjoner, i hvilket tilfelle størrelsen av struct omfatter også plass for den vtbl.

Svarte 23/09/2008 kl. 13:38
kilden bruker

stemmer
3

C-språk forlater kompilatoren en viss frihet om plasseringen av de strukturelle elementer i minnet:

  • minnehull kan forekomme mellom hvilke som helst to komponenter, og etter den siste komponenten. Det var på grunn av det faktum at visse typer objekter på måldatamaskinen kan være begrenset av grensene for adressering
  • "Minne hull" størrelse inkludert i resultatet av sizeof operatør. Sizeof bare omfatter ikke størrelsen av det bøyelige matrisen, som er tilgjengelig i C / C ++
  • Noen implementasjoner av språket lar deg kontrollere minnet utformingen av strukturer gjennom Pragma og kompilatoren alternativer

Den C-språk gir noen forsikring til programmereren elementenes layout i strukturen:

  • kompilatorer som kreves for å tildele en sekvens av komponenter som øker lageradresser
  • Adressen til den første komponent faller sammen med startadressen for konstruksjonen
  • navngitte bits felt kan bli inkludert i strukturen til de nødvendige adresse innretning av tilstøtende elementer

Problemer knyttet til elementer justering:

  • Forskjellige datamaskiner linje kantene på objekter på ulike måter
  • Forskjellige restriksjoner på bredden av bits felt
  • Datamaskiner forskjellig på hvordan du lagrer bytes i et ord (Intel 80x86 og Motorola 68000)

Slik justering fungerer:

  • Volumet som opptas av strukturen blir beregnet som størrelsen av den innrettede enkelt element av en matrise av slike strukturer. Strukturen bør ende, slik at det første element av den neste følgende struktur ikke den bryter kravene til innretting

ps Mer detaljert info finner du her: "Samuel P.Harbison, Guy L.Steele CA Reference, (5.6.2 - 5.6.7)"

Svarte 28/07/2015 kl. 21:25
kilden bruker

stemmer
2

Tanken er at for fart og cache hensyn, bør operander leses fra adresser justert til sin naturlige størrelse. For å gjøre dette skje, vil kompilatoren pads strukturmedlemmer så følgende medlem eller etter struct bli justert.

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

X86-arkitekturen har alltid vært i stand til å hente feiljustert adresser. Det er imidlertid langsommere og når den feilinnretning overlapper to forskjellige minnelinjer, da det evicts to hurtigminnelinjer når en innrettet tilgang ville bare kaste ut en.

Noen arkitekturer faktisk nødt til å felle på feiljustert leser og skriver, og tidlige versjoner av ARM arkitektur (den som utviklet seg til alle dagens mobile prosessorer) ... vel, de faktisk nettopp returnert dårlige data på for dem. (De ignorert lav-ordens biter).

Til slutt, merk at cache linjer kan være vilkårlig stor, og kompilatoren forsøker ikke å gjette på de eller lage en space-vs-hastighet kompromisset. I stedet justerings avgjørelser er en del av ABI og representerer minimum justering som til slutt vil jevnt fylle opp en cache linje.

TL; DR: justering er viktig.

Svarte 24/02/2016 kl. 06:46
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more