Question

Pour clarifier ma question, nous allons commencer avec un exemple de programme:

#include <stdio.h>

#pragma pack(push,1)
struct cc {
    unsigned int a   :  3;  
    unsigned int b   : 16;
    unsigned int c   :  1;
    unsigned int d   :  1;
    unsigned int e   :  1;
    unsigned int f   :  1;
    unsigned int g   :  1;
    unsigned int h   :  1;
    unsigned int i   :  6;  
    unsigned int j   :  6;  
    unsigned int k   :  4;  
    unsigned int l   : 15;
};
#pragma pack(pop)

struct cc c;

int main(int argc, char **argv)

{   printf("%d\n",sizeof(c));
}

La sortie est « 8 », ce qui signifie que les 56 bits (7 octets) Je veux emballer sont emballés dans 8 octets, apparemment perdre un octet entier. Curieux de savoir comment le compilateur a été pose ces bits en mémoire, j'ai essayé d'écrire des valeurs spécifiques à &c, par exemple:.

int main (int argc, char ** argv)

{
unsigned long long int* pint = &c;
*pint = 0xFFFFFFFF;
printf("c.a = %d", c.a);
...
printf("c.l = %d", c.l);
}

On pouvait s'y attendre, sur x86_64 en utilisant Visual Studio 2010, ce qui suit se produit:

*pint = 0x00000000 000000FF :

c[0].a = 7
c[0].b = 1
c[0].c = 1
c[0].d = 1
c[0].e = 1
c[0].f = 1
c[0].g = 0
c[0].h = 0
c[0].i = 0
c[0].j = 0
c[0].k = 0
c[0].l = 0

*pint = 0x00000000 0000FF00 :

c[0].a = 0
c[0].b = 0
c[0].c = 0
c[0].d = 0
c[0].e = 0
c[0].f = 0
c[0].g = 1
c[0].h = 127
c[0].i = 0
c[0].j = 0
c[0].k = 0
c[0].l = 0


*pint = 0x00000000 00FF0000 :

c[0].a = 0
c[0].b = 0
c[0].c = 0
c[0].d = 0
c[0].e = 0
c[0].f = 0
c[0].g = 0
c[0].h = 32640
c[0].i = 0
c[0].j = 0
c[0].k = 0
c[0].l = 0

etc.

Oubliez la portabilité pour un moment et supposons que vous souciez un CPU, un compilateur et un environnement d'exécution. Pourquoi ne peut-VC ++ emballer cette structure en 7 octets? Est-ce une chose mot-longueur? MSDN docs sur #pragma pack dit " l'alignement d'un élément sera sur une limite qui est soit un multiple de n [1, dans mon cas] ou d'un multiple de la taille de l'élément, selon la plus petite « . Quelqu'un peut-il me donner une idée des raisons pour lesquelles je reçois un sizeof 8 et non 7?

Était-ce utile?

La solution

MSVC ++ alloue toujours au moins une unité de mémoire qui correspond au type que vous avez utilisé pour votre champ de bits. Vous avez utilisé unsigned int, ce qui signifie qu'un unsigned int est attribué initialement, et un autre unsigned int AFFECTÉ lorsque le premier est épuisé. Il n'y a pas moyen de forcer MSVC ++ pour couper la partie non utilisée de la deuxième unsigned int.

En fait, MSVC ++ interprète votre unsigned int comme un moyen d'exprimer la exigences d'alignement pour la structure.

Utilisez de petits types pour vos champs de bits (unsigned short et unsigned char) et regrouper les champs de bits afin qu'ils remplissent l'unité allouée entièrement -. De cette façon vous devriez être en mesure d'emballer les choses aussi étroitement que possible

Autres conseils

Bitfields sont stockés dans le type que vous définissez. Puisque vous utilisez unsigned int, et il ne rentre pas dans un seul unsigned int le compilateur doit utiliser un second entier et stocker les 24 derniers bits dans ce dernier entier.

Eh bien, vous utilisez unsigned int qui se trouve être 32 bits dans ce cas. La limite suivante (pour tenir dans le champ de bits) pour unsigned int est de 64 bits => 8 octets.

pst est juste. membres sont alignés sur les limites de 1 octet (ou plus petit, puisqu'il est un champ de bits). La structure globale a une taille de 8, et est aligné sur une limite de 8 octets. Ceci est conforme à la fois la norme et l'option pack. Les documents ne disent jamais il n'y aura pas de rembourrage à la fin.

Pour donner une autre intéressante illustre ce qui se passe, considérons le cas où vous voulez emballer une structure qui traverse une limite de type. Par exemple.

struct state {
    unsigned int cost     : 24; 
    unsigned int back     : 21; 
    unsigned int a        :  1; 
    unsigned int b        :  1; 
    unsigned int c        :  1;
};

Cette structure ne peut pas être emballé en 6 octets en utilisant MSVC pour autant que je sache. Cependant, nous pouvons obtenir l'effet désiré d'emballage en brisant les deux premiers champs:

struct state_packed {
    unsigned short cost_1   : 16; 
    unsigned char  cost_2   :  8;
    unsigned short back_1   : 16; 
    unsigned char  back_2   :  5;
    unsigned char  a        :  1; 
    unsigned char  b        :  1; 
    unsigned char  c        :  1; 
};

Cela peut en effet être emballé dans 6 octets. Cependant, l'accès au champ de coût initial est extrêmement maladroit et laid. Une méthode consiste à jeter un pointeur state_packed à une struct factice spécialisé:

struct state_cost {
    unsigned int cost     : 24;
    unsigned int junk     :  8; 
};

state_packed    sc;
state_packed *p_sc = &sc;

sc.a = 1;
(*(struct state_cost *)p_sc).cost = 12345;
sc.b = 1;

Si quelqu'un connaît une façon plus élégante de le faire, j'aimerais savoir!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top