Comment remplacer le compilateur C en alignant la variable de taille de mot dans la structure à la limite de mot

J'ai une structure spécifiée comme suit

  • Membre 1, 16 bits
  • Membre 2, 32 bits
  • Membre 3, 32 bits

que je vais lire dans un fichier. Je veux lire directement à partir du fichier dans la structure.

Le problème est que le compilateur C alignera les variables m1, m2 et m3 sur les limites de mots qui sont à 32 bits puisque je travaille sur un ARM Cortex M3 pour la déclaration de structure suivante:

typedef struct
{
    uint16_t m1;
    uint32_t m2;
    uint32_t m3;
}something;

Lire directement à partir du fichier mettra des valeurs erronées en m2 et m3, et lit 2 octets supplémentaires aussi.

J'ai piraté et utilise actuellement ce qui suit qui fonctionne très bien:

typedef struct
{
    uint16_t m1;
    struct
    {
        uint16_t lo;
        uint16_t hi;
    }m2;
    struct
    {
        uint16_t lo;
        uint16_t hi;
    }m3;
}something;

Cependant, cela ressemble à un hack vraiment sale. Je ne peux pas m'empêcher de vouloir un moyen plus propre de forcer le compilateur à mettre des moitiés de m2 et de m3 dans des mots différents, même si ce n'est pas optimal.

J'utilise arm-none-eabi-gcc. Je connais l'emballage de bits, mais je suis incapable de contourner cette optimisation.

Edit: Il s'avère que je ne connaissais pas assez à propos de bit-packing: D

0
Bien que je sois conscient de spécifier le nombre de bits pour chaque champ, je n'étais pas totalement au courant de l'attribut packed. Pas étonnant que je ne l'aie pas connu étant donné que j'ai un peu peur de attribut() s. Eh bien, cela semble fonctionner très bien
ajouté l'auteur TSG, source
arm-aucun-eabi-gcc fonctionne très bien avec l'attribut packed (sauf s'il est compilé avec des paramètres bizarres, je suppose ...). Eh bien, peut-être que certaines versions anciennes ne sont pas ...
ajouté l'auteur Eugene Sh., source
Vous voulez dire que votre compilateur ne vous laissera pas déclarer que la structure est emballée?
ajouté l'auteur some user, source

4 Réponses

Vous ne pouvez pas directement lire une telle structure à partir d'un fichier, et vous ne devriez jamais essayer de le faire. Un mauvais alignement peut causer des pièges sur certaines architectures, et vous ne devriez pas compter sur pragma pour corriger cela.

Le presque portable (*) si lire les éléments du fichier dans les éléments struct sauf si vous êtes sûr que la structure a été écrite avec la même architecture et l'alignement (au moins compatible) que vous utilisez pour la lecture.

Donc, pour votre cas d'utilisation, je recommanderais:

fread(&something.m1, sizeof(something.m1), 1, fd);
fread(&something.m2, sizeof(something.m2), 1, fd);
fread(&something.m3, sizeof(something.m3), 1, fd);

(*) il est presque portable car il suppose qu'il n'y a pas de problèmes endiens qui peuvent être corrects ou non selon vos besoins. Si vous êtes sur une seule machine ou sur une seule architecture, c'est bien, mais si vous écrivez struct sur une grosse machine endienne et que vous la lisez sur un petit boutiste, de mauvaises choses se produiront ...

0
ajouté
Ce code est spécifiquement destiné à cette architecture cible. Il est construit pour un système embarqué spécifique, donc je ne prévois pas de tels problèmes. L'endianisme n'est pas un problème ici. Le compilateur gérera l'endianness étant donné l'endianness MCU. Si vous pouviez me diriger vers un endroit où je pourrais trouver des «pièges» potentiels qui seraient utiles.
ajouté l'auteur TSG, source

Peut-être #pragma pack (2) . Cela devrait forcer le compilateur à utiliser l'alignement sur 2 octets

0
ajouté

Ce que vous recherchez est l'attribut packed . Cela forcera gcc à ne pas faire de remplissage autour des membres. Tiré du Documents du GCC en ligne :

emballé

Cet attribut, associé à une définition de type enum, struct ou union, spécifie que la mémoire minimale requise doit être utilisée pour représenter le type. La spécification de cet attribut pour les types struct et union équivaut à spécifier l'attribut packed sur chacun des membres de la structure ou de l'union. La spécification de l'indicateur -fshort-enums sur la ligne équivaut à spécifier l'attribut packed sur toutes les définitions d'énumération.

Vous ne pouvez spécifier cet attribut qu'après une accolade de fermeture sur une définition enum, pas dans une déclaration typedef, à moins que cette déclaration contienne aussi la définition de l'enum.

Donc ce que vous voulez est quelque chose comme:

typedef struct
{
    uint16_t m1;
    uint32_t m2;
    uint32_t m3;
} __attribute__ ((packed)) something;

En outre, je vous recommande d'utiliser une vérification de l'assertion de compilation pour vous assurer que la taille de la structure correspond bien à ce que vous voulez. être.

0
ajouté
__attribute__ ((aligned (2)));
0
ajouté
Selon les documents du GCC: L'attribut aligné ne peut qu'accroître l'alignement; mais vous pouvez le diminuer en spécifiant emballé aussi bien.
ajouté l'auteur missimer, source