8
votes

Que fait VC ++ lors de l'emballage des champs de bit?

Pour clarifier ma question, commençons avec un exemple de programme: xxx

La sortie est "8", ce qui signifie que les 56 bits (7 octets) que je veux packer sont emballés. dans 8 octets, apparemment gaspillant un octet entier. Curieux de savoir comment le compilateur posa ces morceaux dans la mémoire, j'ai essayé de rédiger des valeurs spécifiques sur & C , par exemple:

int Main (int Argc, Char ** argv) xxx

de manière prévisible, sur x86_64 à l'aide de Visual Studio 2010, ce qui suit se produit: xxx

etc.

Oubliez la portabilité pendant un moment et supposez que vous vous souciez d'une CPU, d'un compilateur et d'un environnement d'exécution. Pourquoi VC ++ ne peut-il pas emballer cette structure en 7 octets? Est-ce une chose de mots? MSDN Docs sur #pragma pack dit "L'alignement d'un membre sera sur une limite qui est soit un multiple de N [1 dans mon cas], soit un multiple de la taille du membre, selon la taille inférieure." Quelqu'un peut-il me donner une idée de la raison pour laquelle je reçois une taille de 8 et non 7?


1 commentaires

La documentation indique "... sera sur une frontière qui est ..."; Cependant, je ne peux pas trouver où il est dit quelque chose sur une garantie de taille.


5 Réponses :


3
votes

Les champs de bit sont stockés dans le type que vous définissez. Puisque vous utilisez non signé INT , et il ne contient pas dans un seul non signé INT puis le compilateur doit utiliser un deuxième entier et stocker les 24 derniers bits dans ce dernier entier .


2 commentaires

Non c'est faux. Le compilateur peut stocker des champs de bits dans n'importe quel type qu'il souhaite.


@ABYX Cela va à l'encontre de toutes les autres réponses et commencient ici. Si vous avez quelque chose à soutenir votre affirmation, nous aimerions le voir.



1
votes

Eh bien, vous utilisez Unsigné Int qui se trouve être 32 bits dans ce cas. La limite suivante (pour s'adapter au Bitfield) pour UNSIGED INT est 64 bits => 8 octets.


0 commentaires

0
votes

PST a raison. Les les membres sont alignés sur les limites de 1 octet (ou plus petit, puisqu'il s'agit d'un bitfield). La structure générale a la taille 8 et est alignée sur une limite de 8 octets. Cela est conforme à la standard et à l'option . Les docs ne disent jamais qu'il n'y aura pas de rembourrage à la fin.


11 commentaires

La question n'est pas avec le rembourrage à la fin, mais avec le fait que les champs de bit sont emballés dans des unités intérieures du type Bitfield. La structure n'a pas de rembourrage du tout, seulement deux non signé INT .


@David, la norme indique (§6.7.2.1), "Un bit-Fi est interprété comme un type d'entiers signé ou non signé composé du nombre spécifié de bits. [...] Une implémentation peut allouer une unité de stockage adressable suffisamment grande tenir un bit-fid. " Donc, je ne pense pas que non signé INT signifie qu'il doit réellement utiliser non signé INT comme unité de stockage, juste un type non signé avec suffisamment de bits. De plus, les champs de bits sont autorisés à interrompre les limites de l'unité de stockage. Donc, je pense qu'il y a du rembourrage à la fin.


BTW, quelle standard cherchez-vous? Ni la norme C ++ actuelle ni la FCD C ++ 0x ont la section 6.7.2.1.


Cette partie de la norme me confondre au moins. Ma justification de cet argument est que le §9.6 / 1 contient: l'expression constante [Nombre de bits] peut être plus grande que le nombre de bits dans la représentation de l'objet (3.9) du type de champ de bit; Dans ce cas, les bits supplémentaires sont utilisés comme bits de rembourrage et ne participent pas à la représentation de la valeur (3.9) du Bitfield.


Cela signifie que les bits de caractères non signé: 10 ne seront pas en mesure de stocker un entier 10 bits (dans une machine 8 bits / charme) et seront différents de non signé courts bits: 10 , (supposant 16 bits bref int). À partir de celui que j'ai déduit - Je ne peux pas indiquer la norme ..., que si le compilateur n'est pas capable de tomber jusqu'à un type de représentation plus gros, il n'est probablement pas autorisé à descendre non plus. Il faudrait aussi casser le type de représentation à partir de int à short + char dans ce cas particulier (à nouveau en supposant que 8bit Char, 16bit Short, 32bit Int)


@David, désolé, ça vient de C99. Je n'ai pas entendu dire que C ++ est différent dans ce domaine, mais cela pourrait être.


@David, cependant, c ++ 0x §9.6 dit: "Les champs de bit sont emballés dans une unité d'allocation adressable. [Remarque: les unités d'allocation à chevauchements de bit sur certaines machines et non sur d'autres. Les bit-fi sont attribués à droite. -Left sur certaines machines, à gauche à droite sur les autres. - Note de fin] ", ce qui semble assez similaire à C99. À ma lecture, il ne dit pas que "l'unité d'allocation" (qui semble être la version C ++ de "Storage Unit") doit être un non signé INT (à nouveau à nouveau sur le code de l'OP).


@Matthew: Non, dans toute la section, la seule référence au type de sous-étage est la citation que j'ai ajoutée ci-dessus (§9.6 / 1) qui indique clairement que la taille déclarée de l'objet sous-jacent a un effet sur la sémantique du champ. (Si le type déclaré est inférieur au nombre de bits demandés, les bits en excès ne font pas partie de la valeur, mais servent de rembourrage) il n'impose pas explicitement comment le type sous-jacent est défini, outre le fait que la grammaire définit La déclaration de champ de bits en tant que Attribut d'identificateur de spécificateur déclineur: Expression constante


... avec Decl-Specififier défini comme (une des options) Spécificateur de type . Maintenant, sans autre référence à Type disponible dans le texte, je ne peux que supposer que type sous-jacent fait référence au spécificateur de type dans le < i> déclin-spécificateur , lequel dans l'exemple est non signé INT . C'est pourquoi j'ai déjà mentionné Avant que cette partie de la norme ne semble pas vraiment Clear pour moi.


@David, je ne voulais pas dire que le type déclaré n'a aucun effet. Vous avez clairement raison que la clause Bits de rembourrage est basée sur le type déclaré. Mon point principal est que l'unité de stockage / l'unité d'allocation ne doit pas nécessairement être identique au type déclaré. Je suis d'accord que la norme pourrait être plus claire.


@Matthew: Nous acceptons ensuite de ne pas être d'accord :), et en ce que la norme n'est pas aussi claire que possible. En tout cas, même si j'ai accepté votre justification, il n'ya pas de combinaison de types de stockage que le compilateur peut prendre pour l'entrée donnée qui maintiendrait la sémantique et ne prendrait que 7 octets de stockage sans modifier le comportement prévu selon le §9.6 / 1. . Donc, retour à la carré, il n'a rien à voir avec le rembourrage à la fin de la structure.



7
votes

MSVC ++ attribue toujours au moins une unité de mémoire correspondant au type que vous avez utilisé pour votre champ de bit. Vous avez utilisé non signé INT , ce qui signifie qu'un non signé INT est attribué initialement, et un autre non signé INT est attribué lorsque le premier est épuisé. Il n'y a aucun moyen de forcer MSVC ++ à couper la partie inutilisée du second non signé INT .

Fondamentalement, MSVC ++ interprète votre non signé INT comme moyen d'exprimer les exigences alignement pour toute la structure.

Utilisez des types plus petits pour vos champs de bits ( non signé court et non signé char ) et regroupez les champs de bits afin qu'ils remplissent entièrement l'unité allouée - de cette façon de vous devrait être capable d'emballer les choses aussi étroites que possible.


4 commentaires

Dans ce cas, il ne peut rien sauver s'il en a besoin des 15 et 16 bits, il préfère se retrouver avec au moins 9 octets parce qu'il utilise déjà 56 bits.


Non ... avec ce conseil, je peux effectivement l'emballer dans 7 octets (merci Andreyt). Je n'ai aucune idée de ce que vous voulez dire d'environ 9 octets.


@Rooke: Notez que Non signé Char Bits: 10 est valide, mais probablement pas ce que vous voulez dire (ne stockera que 8 bits Valeurs et réserver 2 bits supplémentaires pour le rembourrage). C'est à dire. Il est d'une importance cruciale de regrouper les champs de bit de manière à ce que chaque champ de bit fits dans l'unité allouée.


@David Rodríguez - Dribesas: En effet. La solution nécessitait la réorganisation des champs.



0
votes

Pour donner un autre intéressant illustre ce qui se passe, considérez le cas où vous souhaitez emballer une structure qui traverse une limite de type. E.G.

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;


0 commentaires