champ de bits de padding dans C
Continue mes expériences en C, je voulais voir comment les champs de bits sont placés dans la mémoire. Je suis en train de travailler sur des processeurs Intel 64 bits de la machine. Voici mon bout de code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
int main(int argc, char**argv){
struct box_props
{
unsigned int opaque : 1;
unsigned int fill_color : 3;
unsigned int : 4;
unsigned int show_border : 1;
unsigned int border_color : 3;
unsigned int border_style : 2;
unsigned int : 2;
};
struct box_props s;
memset(&s, 0, 32);
s.opaque = 1;
s.fill_color = 7;
s.show_border = 1;
s.border_color = 7;
s.border_style = 3;
int i;
printf("sizeof box_porps: %d sizeof unsigned int: %d\n", sizeof(struct box_props), sizeof(unsigned int));
char *ptr = (char *)&s;
for (i=0; i < sizeof(struct box_props); i++){
printf("%x = %x\n", ptr + i, *(ptr + i));
}
return 0;
et voici un sortie:
sizeof box_porps: 4 sizeof unsigned int: 4
5be6e2f0 = f
5be6e2f1 = 3f
5be6e2f2 = 0
5be6e2f3 = 0
Et voici la question: pourquoi struct box_props
a taille 4
- ne peut pas juste être 2
octets ? Comment rembourrage est fait dans ce cas ? Je suis peu (nomen omen) confondue avec elle.
Thx d'avance pour toutes les réponses
Je pense que c'est parce que vous êtes à l'aide de type int, essayez d'utiliser short int si vous voulez la taille 2.essayer d'envoyer un feedback
memset(&s, 0, 32);
Ouch! Vous vous demandez memset
à zéro de 32 octets, mais ont seulement quatre. Vous êtes juste malchanceux qui n'avaient pas de détruire une partie essentielle de la pile, et qui a causé votre programme de crash violemment.OriginalL'auteur JosiP | 2013-05-22
Vous devez vous connecter pour publier un commentaire.
Même si le besoin total est à seulement 2 Octets (1+3+4+1+3+2+2) dans ce cas, la taille du type de données utilisées (
unsigned int
) est de 4 octets. Si la mémoire allouée est également de 4 Octets. Si vous voulez seulement 2 Octets alloués utiliserunsigned short
que votre type de données et exécutez à nouveau le programme.OriginalL'auteur Deepu
De la norme ISO C standard:
Donc il n'y a pas d'obligation de toujours choisir la plus petite partie de la mémoire pour la structure. Et depuis 32 bits mots sont probablement la taille d'origine de votre compilateur, c'est ce qu'il choisit.
uint32_t foo:4;
va allouerfoo
quatre bits à partir d'un emplacement de stockage de typeuint32_t
; à moins que les quatre bits restent d'un immédiatement précédent champ de bits de même type, il va allouer un autreuint32_t
. Je n'aime pas ces règles, mais elles sont ce qu'elles sont.OriginalL'auteur Imre Kerr
Le placement de bit-champs dans la mémoire ne dépend pas seulement sur la façon dont le compilateur décide d'attribuer les différents domaines au sein de la structure, mais aussi sur la endian-ness de la machine sur laquelle vous êtes en cours d'exécution. Permet de prendre ces un par un. L'affectation des champs dans le compilateur peut être contrôlée par la spécification de la taille du champ (comme @DDD), mais aussi par le biais d'un autre mécanisme. Vous pouvez dire que le compilateur soit
pack
votre structure ou à laisser mieux adapté pour comment le compilateur peut vouloir optimiser l'architecture de la machine pour laquelle vous êtes en cours de compilation. L'emballage est spécifié à l'aide de lapacked
l'attribut type. Ainsi, si vous spécifiez la structure:vous pouvez ainsi voir une mise en page différente dans la mémoire. Notez que vous ne verrez pas la mise en page différente en examinant les éléments de la structure - son la mise en mémoire qui pourrait changer. L'emballage d'une structure est d'une importance cruciale lors de la communication avec autre chose comme un appareil IO qui s'attend à des bits spécifiques à des endroits spécifiques.
La deuxième question avec champ de bits structures est leur mise en page de la dépendance sur les endian-ness. La mise en page de la structure dans la mémoire (ou des données) dépend de si vous êtes en cours d'exécution sur un big-endian (PUISSANCE) ou little-endian (par exemple, x86) de la machine. Certains systèmes (par exemple, les systèmes PowerPC embarqués sont bi-endian).
En général, les champs de bits, il est très difficile de port code parce que vous êtes futzing avec la mise en page de données dans la mémoire.
Espérons que cette aide!
OriginalL'auteur Ram Rajamony
Pour une raison que je ne pas comprendre, la mise en œuvre de la norme C décidé que la spécification d'un numberic type avec un champ de bits doit allouer de l'espace suffisant pour tenir que de type numérique, à moins que le précédent, le champ est un champ de bits, affectés du même type, qui avait juste assez d'espace pour manipuler le champ suivant.
Pour votre exemple, sur une machine 16 bits unsigned short, vous devez modifier les déclarations dans votre champ de bits pour unsigned short. Comme il arrive, unsigned char également de travailler, et de produire les mêmes résultats, mais ce n'est pas toujours le cas. Si de manière optimale-paniers bitfields chevauchent char limites, mais pas à court limites, puis de déclarer bitfields comme
unsigned char
aurait besoin de rembourrage pour éviter de telles chevauchant.Bien que certains processeurs n'ont aucune difficulté à la génération de code pour bitfields qui chevauchent le stockage de l'unité de limites, la présente norme C interdirait l'emballage de cette façon (encore une fois, pour des raisons que je n'ai pas l'imaginer). Sur une machine avec typique 8/16/32/64 bits types de données, par exemple, un compilateur n'a pas pu permettre à un programmeur de spécifier un 3 octets de la structure contenant huit à trois octets des champs, depuis les champs aurait à cheval sur les limites d'octets. Je peux comprendre la spec pas nécessitant compilateurs de manipuler des champs qui chevauchent les frontières, ou d'exiger que bitfields être mis dans un mode particulier (j'avais les considérer comme infiniment plus utile si l'on peut spécifier qu'un champ de bits doit par exemple utiliser les bits 4 à 7 de un certain endroit), mais la présente norme semble donner le pire des deux mondes.
Dans tous les cas, la seule façon d'utiliser bitfields efficacement est de savoir où l'unité de stockage de limites, et de choisir les types de la bitfields convenablement.
PS - Il est intéressant de noter que, si je me souviens compilateurs utilisés pour interdire
volatile
des déclarations pour les structures contenant des bitfields (depuis la séquence des opérations lors de l'écriture d'un champ de bits ne peut pas être bien définie), en vertu des nouvelles règles de la sémantique pourrait être bien définis (je ne sais pas si la spécification exige en fait). Par exemple, étant donné:la déclaration
bar.b3 = 123;
va lire les 64 premiers bits debar
, puis écrire les 64 premiers bits debar
avec une mise à jour de la valeur. Sibar
n'ont pas été volatile, un compilateur peut remplacer cette séquence avec un simple 8-bits à écrire, maisbar
pourrait être quelque chose comme un matériel de registre qui peut uniquement être écrit en 32-bits ou 64-bits morceaux.Si j'avais tenait qu'à moi, il serait possible de définir bitfields en utilisant quelque chose comme:
indiquant que le débit en bauds est de 13 bits de départ au bit 0 (LSB), le format est de 3 bits à partir après le débit en bauds, enableRxStartInt est peu 28, etc. Une telle syntaxe permettrait à de nombreux types de données, l'emballage et le déballage d'être écrite de façon nomade, et devrait permettre à beaucoup d'I/O enregistrer les manipulations à faire dans un compilateur indépendant de la mode (même si un tel code serait évidemment de matériel spécifique).
Aussi 6.7.2.1:10: “Une mise en œuvre peut attribuer tous adressable de l'unité de stockage assez grand pour contenir un peu de terrain”
Dans certains anciens compilateurs C, je crois que le seul aspect d'un champ de bits type, le compilateur aurait soucier est de savoir s'il a été signé ou non signé, mais sur les nouveaux compilateurs j'ai vu toute particulière champ de bits seront toujours attribués dans le cadre d'un emplacement de stockage de son type indiqué. Par exemple, si on a un volatile struct avec un 8-bit champ de bits de type
uint64_t
, même si il est aligné sur un octet, le code généré va lire les 64 bits de l'unité de stockage contenant le champ de bits, changement de 8 bits, et écrire tous les 64 bits.Et 6.7.2.1 (11) dit: "Si l'espace est insuffisant reste, si un peu de champ qui ne correspond pas à y mettre de l'unité ou des chevauchements unités adjacentes est mise en œuvre définies.", ainsi, chevauchant est explicitement autorisé. gcc et clang straddle bits-champs et l'utilisation de plus petites unités de stockage qu'il serait nécessaire, pour le type nommé, si vous leur dites en utilisant
__attribute__((packed))
au moins sur certaines architectures.Cela dépend de ce que vous considérez votre syntaxe idée. Je ne pense pas que
= bit_position
est un moyen particulièrement utile de le préciser (mais je n'ai pas de meilleure idée). Si vous voulez dire à l'idée d'être en mesure de spécifier la mise en page exacte de bits séquences, qui, je crois, être très utile parfois. Mais il ne va pas se produire, le comité est très loin de se limiter implémentations donc fortement, autant que je puis dire.OriginalL'auteur supercat