C Language
Structure rembourrage et emballage
Recherche…
Introduction
Par défaut, les compilateurs C disposent de structures permettant d'accéder rapidement à chaque membre, sans encourir de pénalités pour un accès non aligné, un problème avec les machines RISC telles que DEC Alpha et certains processeurs ARM.
Selon l'architecture du processeur et le compilateur, une structure peut occuper plus d'espace en mémoire que la somme des tailles des membres de ses composants. Le compilateur peut ajouter un remplissage entre les membres ou à la fin de la structure, mais pas au début.
L'emballage remplace le remplissage par défaut.
Remarques
Eric Raymond a un article sur The Lost Art de C Structure Packing qui est une lecture utile.
Structures d'emballage
Par défaut, les structures sont complétées en C. Si vous souhaitez éviter ce comportement, vous devez le demander explicitement. Sous GCC, c'est __attribute__((__packed__))
. Considérez cet exemple sur une machine 64 bits:
struct foo {
char *p; /* 8 bytes */
char c; /* 1 byte */
long x; /* 8 bytes */
};
La structure sera automatiquement remplie pour avoir 8-byte
alignement de 8-byte
et ressemblera à ceci:
struct foo {
char *p; /* 8 bytes */
char c; /* 1 byte */
char pad[7]; /* 7 bytes added by compiler */
long x; /* 8 bytes */
};
Donc, sizeof(struct foo)
nous donnera 24
au lieu de 17
. Cela s'est produit à cause d'un compilateur 64 bits en lecture / écriture depuis / vers la mémoire dans 8 octets de mots à chaque étape et évident lorsque vous essayez d'écrire un caractère char c;
un octet en mémoire, un octet complet (c'est-à-dire un mot) récupéré et consomme uniquement son premier octet et ses sept octets successifs reste vide et inaccessible pour toute opération de lecture et d'écriture pour le remplissage de la structure.
Emballage de structure
Mais si vous ajoutez l'attribut packed
, le compilateur n'ajoutera pas de remplissage:
struct __attribute__((__packed__)) foo {
char *p; /* 8 bytes */
char c; /* 1 byte */
long x; /* 8 bytes */
};
Maintenant, sizeof(struct foo)
retournera 17
.
Les structures généralement emballées sont utilisées:
- Pour économiser de l'espace
- Formater une structure de données pour la transmettre sur le réseau sans dépendre de chaque alignement d'architecture de chaque nœud du réseau.
Il faut tenir compte du fait que certains processeurs, tels que ARM Cortex-M0, ne permettent pas un accès mémoire non aligné; dans de tels cas, le compactage de la structure peut entraîner un comportement indéfini et provoquer un blocage du processeur.
Rembourrage de la structure
Supposons que cette struct
soit définie et compilée avec un compilateur 32 bits:
struct test_32 {
int a; // 4 byte
short b; // 2 byte
int c; // 4 byte
} str_32;
On pourrait s'attendre à ce que cette struct
n'occupe que 10 octets de mémoire, mais en imprimant sizeof(str_32)
nous voyons qu'il utilise 12 octets.
Cela s'est produit car le compilateur aligne les variables pour un accès rapide. Un modèle courant est que lorsque le type de base occupe N octets (où N est une puissance de 2 telle que 1, 2, 4, 8, 16 - et rarement plus grande), la variable doit être alignée sur une limite de N octets ( un multiple de N octets).
Pour la structure affichée avec sizeof(int) == 4
et sizeof(short) == 2
, une disposition commune est:
-
int a;
stocké au décalage 0; taille 4 -
short b;
stocké au décalage 4; taille 2 - remplissage sans nom à l'offset 6; taille 2
-
int c;
stocké au décalage 8; taille 4
Ainsi, struct test_32
occupe 12 octets de mémoire. Dans cet exemple, il n'y a pas de remplissage de fin.
Le compilateur s'assurera que toutes les variables struct test_32
sont stockées à partir d'une limite de 4 octets, afin que les membres de la structure soient correctement alignés pour un accès rapide. Les fonctions d'allocation de mémoire telles que malloc()
, calloc()
et realloc()
sont nécessaires pour garantir que le pointeur renvoyé est suffisamment bien aligné pour être utilisé avec n'importe quel type de données.
Vous pouvez vous retrouver avec des situations bizarres comme sur un processeur Intel x86_64 64 bits (par exemple Intel Core i7 - un Mac sous MacOS Sierra ou Mac OS X), où lors de la compilation en mode 32 bits, les compilateurs placent un double
alignement sur un Limite de 4 octets; mais, sur le même matériel, lors de la compilation en mode 64 bits, les compilateurs placent le double
aligné sur une limite de 8 octets.