Pourquoi la taille d'une structure n'est-elle pas égale à la somme de la taille de chaque membre?

pourquoi l'opérateur sizeof renvoie-t-il une taille plus grande pour une structure que la taille totale des membres de la structure?

565
demandé sur tilz0R 2008-09-23 08:24:47

11 réponses

ceci est dû au rembourrage ajouté pour satisfaire aux contraintes d'alignement. structure de Données d'alignement impacts à la fois de la performance et de l'exactitude des programmes:

  • un accès mal aligné peut être une erreur importante (souvent SIGBUS ).
  • accès mal aligné pourrait être une erreur douce.
    • soit corrigé en matériel, pour une performance modeste-dégradation.
    • ou rectifiée par émulation dans le logiciel, pour une dégradation sévère des performances.
    • en outre, l'atomicité et d'autres garanties de concurrence peuvent être brisées, conduisant à des erreurs subtiles.

voici un exemple utilisant les paramètres typiques d'un processeur x86 (tous les modes 32 et 64 bits utilisés):

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

on peut minimiser la taille des structures en triant les éléments par alignement (le tri par taille suffit pour que dans les types de base) (comme la structure Z dans l'exemple ci-dessus).

REMARQUE IMPORTANTE: Les normes C et C++ précisent que l'alignement de la structure est défini par la mise en œuvre. Par conséquent, chaque compilateur peut choisir d'aligner les données différemment, ce qui entraîne des configurations de données différentes et incompatibles. Pour cette raison, lorsqu'on traite avec des bibliothèques qui seront utilisées par différents compilateurs, il est important de comprendre comment les compilateurs alignent les données. Certains compilateurs ont paramètres de ligne de commande et/ou instructions spéciales #pragma pour modifier les paramètres d'alignement de la structure.

551
répondu Kevin 2017-07-05 20:25:02

Emballage et alignement des octets, comme décrit dans la FAQ C ici :

C'est pour l'alignement. Beaucoup de processeurs ne peuvent pas accéder à 2-et 4-octets les quantités (par exemple, les entrées et les entrées longues) si elles sont entassées chaque-qui-chemin.

supposons que vous ayez cette structure:

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

Maintenant, vous pourriez penser qu'il devrait être possible de ce pack structure en mémoire comme ceci:

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

mais c'est beaucoup, beaucoup plus facile sur le processeur si le compilateur organise comme ceci:

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

dans la version emballée, noter comment il est au moins un peu difficile pour toi et moi pour voir comment les champs b et c s'enroulent? En un mot, il est difficile pour le processeur, trop. Par conséquent, la plupart des compilateurs vont pad la structure (comme avec des champs invisibles supplémentaires) comme ceci:

+-------+-------+-------+-------+
|           a           | pad1  |
+-------+-------+-------+-------+
|       b       |     pad2      |
+-------+-------+-------+-------+
|               c               |
+-------+-------+-------+-------+
|           d           | pad3  |
+-------+-------+-------+-------+
148
répondu EmmEff 2015-11-30 23:33:40

si vous voulez que la structure ait une certaine taille avec GCC par exemple utiliser __attribute__((packed)) .

sur Windows vous pouvez définir l'alignement à un byte en utilisant le cl.exe compier avec l'option /Zp .

il est généralement plus facile pour le CPU d'accéder aux données qui sont un multiple de 4 (ou 8), en fonction de la plate-forme et aussi sur le compilateur.

Donc, c'est une question d'alignement fondamentalement.

Vous devez avoir de bonnes raisons de le changer.

23
répondu INS 2013-08-29 15:26:46

cela peut être dû à l'alignement des octets et au rembourrage de sorte que la structure sorte à un nombre pair d'octets (ou de mots) sur votre plate-forme. Par exemple, dans C on Linux, les 3 structures suivantes:

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

ont des membres dont la taille (en octets) est de 4 octets (32 bits), 8 octets (2x 32 bits) et 1 octet (2+6 bits) respectivement. Le programme ci-dessus (sur Linux avec gcc) imprime les tailles 4, 8, et 4 - où la dernière structure est matelassée pour qu'il est un seul mot (4 octets 8 bits sur ma plate-forme 32 bits).

oneInt=4
twoInts=8
someBits=4
11
répondu Kyle Burton 2014-01-09 12:53:22

voir aussi:

pour Microsoft Visual C:

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

et la compatibilité de la revendication GCC avec le compilateur de Microsoft.:

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

en plus des réponses précédentes, s'il vous plaît noter que indépendamment de la de l'emballage, de il n'y a pas de membres-commande-garantie en C++ . Les compilateurs peuvent (et certainement le font) ajouter des membres virtuels de pointeur de table et des structures de base à la structure. Même l'existence de la table virtuelle n'est pas garantie par la norme (la mise en œuvre du mécanisme virtuel n'est pas spécifiée) et on peut donc conclure qu'une telle garantie est tout simplement impossible.

je suis tout à fait sûr membre-ordre est garanti en C , mais je ne compterais pas là-dessus, quand j'écris un programme multiplateforme ou multi-compilateur.

9
répondu lkanab 2017-04-24 21:45:36

la taille d'une structure est supérieure à la somme de ses parties en raison de ce qu'on appelle l'emballage. Un processeur particulier a une taille de données préférée avec laquelle il fonctionne. Taille préférée de la plupart des processeurs modernes si 32 bits (4 octets). Accéder à la mémoire lorsque les données sont sur ce type de limite est plus efficace que les choses qui chevauchent cette limite de taille.

par exemple. Considérons la structure simple:

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

si la machine est une machine 32 bits et des données sont alignées sur une frontière 32 bits, nous voyons un problème immédiat (en supposant aucun alignement de structure). Dans cet exemple, supposons que les données de structure commencent à l'adresse 1024 (0x400 - notez que les 2 bits les plus bas sont zéro, donc les données sont alignées à une limite de 32 bits). L'accès aux données.a fonctionne très bien car il commence sur une limite-0x400. L'accès aux données.b fonctionnera aussi bien, parce qu'il est à l'adresse 0x404 - une autre limite de 32 bits. Mais un non alignés structure mettrait des données.c à l'adresse 0x405. Les 4 octets de données.c sont à 0x405, 0x406, 0x407, 0x408. Sur une machine 32 bits, le système lisait les données.c pendant un cycle de mémoire, mais n'obtiendrait que 3 des 4 octets (le 4ème octet est à la limite suivante). Ainsi, le système devrait faire un deuxième accès de mémoire pour obtenir le 4ème byte,

Maintenant, si au lieu de mettre des données.c à l'adresse 0x405, le compilateur a rembourré la structure de 3 octets et mis des données.c à l'adresse 0x408, puis le le système n'aurait besoin que d'un cycle pour lire les données, réduisant le temps d'accès à cet élément de données de 50%. Padding échange l'efficacité de mémoire pour l'efficacité de traitement. Étant donné que les ordinateurs peuvent avoir d'énormes quantités de mémoire (plusieurs gigaoctets), les compilateurs estiment que le swap (vitesse par rapport à la taille) est raisonnable.

Malheureusement, ce problème devient un tueur lorsque vous tentez d'envoyer des structures sur un réseau ou même écrire des données binaires dans un fichier binaire. Le rembourrage inséré entre les éléments d'une structure ou d'une classe peuvent perturber les données envoyées au fichier ou au réseau. Afin d'écrire du code portable (un qui ira à plusieurs compilateurs différents), vous aurez probablement à accéder à chaque élément de la structure séparément pour assurer le bon "packing".

d'un autre côté, différents compilateurs ont des capacités différentes pour gérer l'empaquetage de la structure des données. Par exemple, dans Visual C/C++, le compilateur supporte la commande #pragma pack. Ce sera permet d'ajuster l'empaquetage et l'alignement des données.

par exemple:

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

I = sizeof(myData);

je devrais avoir la longueur de 11. Sans le pragma, je pourrais être n'importe quoi de 11 à 14 (et pour certains systèmes, jusqu'à 32), en fonction de l'empaquetage par défaut du compilateur.

6
répondu sid1138 2015-06-12 06:50:22

Il peut le faire si vous avez implicitement ou explicitement définir l'alignement de la structure. Une structure qui est aligné 4 sera toujours un multiple de 4 octets, même si la taille de ses membres serait quelque chose qui n'est pas un multiple de 4 octets.

aussi une bibliothèque peut être compilée sous x86 avec des ints 32 bits et vous pouvez comparer ses composants sur un processus 64 bits vous donnerait un résultat différent si vous faisiez cela à la main.

5
répondu Orion Adrian 2008-09-23 04:27:07

C99 N1256 projet de norme

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

6.5.3.4 la taille de l'opérateur :

3 lorsqu'elle s'applique à une opérande qui a une structure ou un type d'union, le résultat est le nombre total d'octets dans un tel objet, y compris le rembourrage intérieur et arrière.

6.7.2.1 spécificateurs de Structure et d'union :

13 ... Il peut être sans nom rembourrage à l'intérieur d'un objet de structure, mais pas au début.

et:

15 Il peut y avoir un rembourrage sans nom à l'extrémité d'une structure ou d'une union.

La nouvelle C99 flexible membre de la pile de la fonction ( struct S {int is[];}; ) peut également affecter le rembourrage:

16 en tant que cas particulier, le dernier élément d'une structure comportant plus d'un membre désigné peut avoir un type de tableau incomplet; ceci est appelé un membre de tableau flexible. Dans la plupart des situations, le membre flexible du tableau est ignoré. En particulier, la taille de la structure est comme si l' l'élément flexible de la matrice a été omis, sauf qu'il peut avoir plus de rembourrage arrière que l'omission impliquerait.

de l'Annexe J des Problèmes de Portabilité réitère:

les termes suivants ne sont pas spécifiés :..

  • La valeur des octets de remplissage lors de stocker des valeurs dans des structures ou des syndicats (6.2.6.1)

C++11 N3337 projet de norme

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

5.3.3 Sizeof :

2 pour une classe, le résultat est le nombre d'octets dans un objet de cette classe, y compris toute rembourrage nécessaire pour placer les objets de ce type dans un tableau.

9.2 les membres de la Classe :

A le pointeur vers un objet struct standard-layout, convenablement converti à l'aide d'un reinterpret_cast, pointe vers son membre initial (ou si ce membre est un bit-field, alors à l'unité dans laquelle il réside) et vice versa. [ Note: Il peut donc y avoir un rembourrage sans nom dans un objet de structure standard, mais pas au début., que nécessaire afin d'obtenir l'alignement. - la note de fin ]

je sais seulement assez de C++ pour comprendre la remarque :-)

5

en plus des autres réponses, une structure peut (mais n'a généralement pas) des fonctions virtuelles, auquel cas la taille de la structure inclura également l'espace pour le vtbl.

4
répondu JohnMcG 2008-09-23 13:38:11

du langage C, les feuilles compilateur d'une certaine liberté quant à l'emplacement des éléments de structure dans la mémoire:

  • des trous de mémoire peuvent apparaître entre deux composants, et après le dernier composant. Il était dû au fait que certains types d'objets sur l'ordinateur cible peuvent être limités par les limites de l'adressage
  • "trous de mémoire" taille inclus dans le résultat de l'opérateur sizeof. La taille de seulement ne fait pas inclure la taille du tableau flexible, qui est disponible en C/C++
  • quelques implémentations du langage vous permettent de contrôler la disposition de la mémoire des structures à travers les options pragma et compilateur

le langage C fournit une certaine assurance au programmeur de la disposition des éléments dans la structure:

  • compilateurs nécessaires pour assigner une séquence de composants augmentant la mémoire adresses 151960920"
  • L'adresse du premier composant coïncide avec l'adresse de début de la structure
  • des champs de bits sans nom peuvent être inclus dans la structure pour les alignements d'adresse requis des éléments adjacents

problèmes relatifs aux éléments alignement:

"
  • Différents ordinateurs de la ligne sur les bords des objets de différentes manières
  • Différentes restrictions sur la largeur du champ bit
  • les ordinateurs diffèrent sur la façon de stocker les octets dans un mot (Intel 80x86 et Motorola 68000)

Comment l'alignement des œuvres:

  • le volume occupé par la structure est calculé comme la taille de l'élément simple aligné d'un réseau de telles structures. La structure doit fin de sorte que le premier élément du suivant la structure ne viole pas les exigences de l'alignement

p. s des informations plus détaillées sont disponibles ici: "Samuel P. Harbison, Guy L. Steele C A Reference, (5.6.2 - 5.6.7)"

3
répondu bruziuz 2015-07-29 06:38:59

l'idée est que pour des considérations de vitesse et de cache, les opérandes doivent être lues à partir d'adresses alignées à leur taille naturelle. Pour ce faire, les membres de la structure des pads du compilateur afin que le membre suivant ou la structure suivante soient alignés.

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

l'architecture x86 a toujours été capable de récupérer des adresses mal alignées. Cependant, c'est plus lent et quand le désalignement chevauche deux lignes de cache différentes, alors il évacue deux lignes de cache quand un aligné d'accès à seulement expulser.

certaines architectures ont en fait à piéger sur les lectures et les Écritures mal alignées, et les premières versions de L'architecture ARM (celle qui a évolué dans tous les CPU mobiles d'aujourd'hui) ... en fait, ils ont juste retourné de mauvaises données pour ceux-là. (Ils ont ignoré les éléments de bas ordre.)

enfin, notez que les lignes de cache peuvent être arbitrairement grandes, et que le compilateur ne tente pas de deviner ces lignes ou de faire un espace-vs-speed compromis. Au lieu de cela, les décisions d'alignement font partie de L'ABI et représentent l'alignement minimum qui finira par remplir uniformément une ligne de cache.

TL;DR: l'alignement est important.

2
répondu DigitalRoss 2016-02-24 06:46:13