web-dev-qa-db-fra.com

Qu'est-ce qu'une structure "compacte" en C?

Je vais bien que du code C écrit pour le compilateur Microchip C30 et je vois souvent des structures définies comme suit:

typedef struct __attribute__((__packed__)) 
{
    IP_ADDR     MyIPAddr;               // IP address
    IP_ADDR     MyMask;                 // Subnet mask
    IP_ADDR     MyGateway;              // Default Gateway
        // etc...
} APP_CONFIG;

Que signifie emballé?

31
PICyourBrain

Lorsque des structures sont définies, le compilateur est autorisé à ajouter des remplissages (espaces sans données réelles) afin que les membres tombent dans des limites d'adresse plus faciles d'accès pour le CPU.

Par exemple, sur un processeur 32 bits, les membres 32 bits doivent commencer à des adresses qui sont multiples de 4 octets afin d'être efficacement accessibles (lues et écrites). La définition de structure suivante ajoute un remplissage de 16 bits entre les deux membres, de sorte que le deuxième membre tombe dans une limite d'adresse appropriée:

struct S {
    int16_t member1;
    int32_t member2;
};

La structure en mémoire de la structure ci-dessus dans une architecture 32 bits est (~ = padding):

+---------+---------+
| m1 |~~~~|   m2    |
+---------+---------+

Lorsqu'une structure est emballée, ces rembourrages ne sont pas insérés. Le compilateur doit générer plus de code (qui s'exécute plus lentement) pour extraire les membres de données non alignés et également pour y écrire.

La même structure, une fois compressée, apparaîtra en mémoire comme quelque chose comme:

+---------+---------+
| m1 |   m2    |~~~~
+---------+---------+
61
Juliano

Il demande au compilateur de ne pas ajouter de remplissage entre les membres de struct.

Voir, par exemple, cette page .

6
NPE

_attribute__((__packed__)) signifie (très probablement) "ne pas insérer de rembourrage pour accélérer les choses" et peut également signifier "ne pas insérer d'alignements pour préserver l'alignement".

1
Vatine

Permettez-moi d'expliquer le concept de remplissage dans les structures, puis les structures compactées en prenant un exemple.

Et voyons ensuite pourquoi l'emballage est nécessaire.

Rembourrage:

struct eg_struct
{
           unsigned char abc;
           unsigned int  xyz;
}

Lorsque la structure est déclarée comme ci-dessus sur une architecture 16 bits, la variable abc se verra attribuer une adresse. L'adresse suivante n'est pas affectée à la variable xyz, au lieu d'un octet supplémentaire est ajouté, puis l'adresse suivante serait affectée à la variable xyz.

Au final, la structure ressemble à quelque chose comme ci-dessous:

struct eg_struct
{
           unsigned char abc;
           unsigned char paddedbytes[1];
           unsigned int  xyz;
}

Le remplissage rend les adresses des variables membres facilement accessibles au microcontrôleur. L'inconvénient est les octets supplémentaires inutiles qui entrent dans l'image.

Emballage:

Si la même structure est déclarée à l'aide de l'attribut "packed", l'octet supplémentaire ne sera pas ajouté après la variable abc.

Permettez-moi de donner un exemple où l'emballage est nécessaire:

Considérons un microcontrôleur interfacé avec une EEPROM où une certaine structure est stockée.

Imaginez une fonction écrivant dans l'EEPROM comme ci-dessous:

Write_EEPROM(EEPROM address, Ram address, Byte count);

Maintenant, si le conditionnement n'est pas fait, les octets remplis supplémentaires occuperaient de l'espace dans l'EEPROM, ce qui est inutile.

1
Babajan

Une chose qui n'a pas été explicitement mentionnée est que le conditionnement est généralement effectué pour correspondre aux structures de champs prédéfinies. Par exemple, à la couche de bas niveau d'une interface réseau, une série d'octets est échangée entre les machines en réseau. Une fois les données reçues, elles devront être mappées à une structure de haut niveau afin que les données puissent être manipulées facilement. C'est quand aucun remplissage n'est généralement nécessaire, de sorte que la structure mappe directement aux octets.

L'échange de données réseau implique également un problème d'endianité des octets (c'est-à-dire que presque toutes les données réseau utilisent le format big endian, quelle que soit l'endianité des machines source et de destination).

En outre, certaines machines ne peuvent pas accéder à des données étendues dans une adresse non alignée, par exemple, les cœurs Cortex-M0 ne peuvent pas accéder aux données 32 bits dans une adresse alignée non 32 bits, il faut donc faire attention à l'écriture code réseau dans de tels cas.

1