web-dev-qa-db-fra.com

Champs de bits compressés dans les structures c - GCC

Je travaille avec des structures en c sur linux. J'ai commencé à utiliser des champs de bits et l'attribut "emballé" et je suis tombé sur un comportement étrange:

struct t1
{
    int a:12;
    int b:32;
    int c:4;
}__attribute__((packed));

struct t2
{
    int a:12;
    int b;
    int c:4;
}__attribute__((packed));

void main()
{
    printf("%d\n",sizeof(t1)); //output - 6
    printf("%d\n",sizeof(t2)); //output - 7
}

Comment se fait-il que les deux structures - qui sont exactement les mêmes - prennent un nombre d'octets différent?

18
Danny Cohen

Vos structures ne sont pas "exactement les mêmes". Votre premier a trois champs binaires consécutifs, le second a un champ binaire, un int (non-champ binaire), puis un deuxième champ binaire.

Ceci est significatif: les champs binaires consécutifs (largeur non nulle) sont fusionnés en un seul emplacement mémoire, tandis qu'un champ binaire suivi d'un champ non binaire sont des emplacements mémoire distincts.

Votre première structure a un seul emplacement mémoire, votre seconde en a trois. Vous pouvez prendre l'adresse du membre b dans votre deuxième structure, pas dans votre première. Les accès au membre b ne font pas la course avec les accès a ou c dans votre deuxième structure, mais ils le font dans votre premier.

Avoir un champ non binaire (ou un champ binaire de longueur nulle) juste après qu'un membre du champ binaire le "ferme" dans un sens, ce qui suit sera un emplacement/objet de mémoire différent/indépendant. Le compilateur ne peut pas "empaqueter" votre membre b à l'intérieur du champ de bits comme il le fait dans la première structure.

29
Mat
struct t1 // 6 bytes
{
    int a:12; // 0:11
    int b:32; // 12:43
    int c:4;  // 44:47
}__attribute__((packed));

struct t1 // 7 bytes
{
    int a:12; // 0:11
    int b;    // 16:47
    int c:4;  // 48:51
}__attribute__((packed));

Le régulier int b doit être aligné sur une limite d'octets. Il y a donc un rembourrage devant. Si vous mettez c juste à côté de a, ce remplissage ne sera plus nécessaire. Vous devriez probablement le faire, car accéder à des entiers non alignés comme int b:32 est lent.

18
John Zwinck