Quand utiliser des champs de bits en C?

Sur la question "pourquoi avons-nous besoin d'utiliser des bits des champs", dans Google, j'ai trouvé que les champs de bits sont utilisés pour les drapeaux. Maintenant, je suis curieux, est-ce la seule façon dont les champs de bits sont utilisés pratiquement? Devons-nous utiliser des champs de bits pour économiser de l'espace?

Façon de définir le champ de bits à partir du livre:

struct {
unsigned int is_keyword : 1; 
unsigned int is_extern : 1; 
unsigned int is_static : 1;
} flags;

Pourquoi utilisons-nous int? De combien d'espace est occupé? Je suis confus pourquoi nous utilisons int, mais pas court ou smith plus petit que int. Si je comprends bien seulement 1 bit est occupé en mémoire, mais pas l'ensemble valeur int non signée. Est-il correct?

57
c
demandé sur Mgetz 2014-07-24 16:06:48

14 réponses

Maintenant, je suis curieux ,[ sont des drapeaux] la seule façon dont les champs de bits sont utilisés pratiquement?

Non, les drapeaux ne sont pas les seuls moyens d'utiliser les champs de bits. Ils peuvent également être utilisés pour stocker des valeurs supérieures à un bit, bien que les drapeaux soient plus courants. Par exemple:

typedef enum {
    NORTH = 0,
    EAST = 1,
    SOUTH = 2,
    WEST = 3
} directionValues;

struct {
    unsigned int alice_dir : 2;
    unsigned int bob_dir : 2;
} directions;

Devons-nous utiliser des champs de bits pour économiser de l'espace?

Les champs de bits économisent de l'espace. Ils permettent également un moyen plus facile de définir des valeurs qui ne sont pas alignées sur les octets. Plutôt que de décalage de bits et en utilisant des opérations au niveau du BIT, nous pouvons utiliser la même syntaxe que la définition de champs dans un struct. Cela améliore la lisibilité. Avec un bitfield, vous pouvez écrire

directions.bob_dir = SOUTH;

Cependant, en le faisant manuellement, vous devez écrire quelque chose comme:

#define BOB_OFFSET 2
directions &= ~(3<<BOB_OFFSET); // clear Bob's bits
directions |= SOUTH<<BOB_OFFSET;

Cette meilleure lisibilité est sans doute plus importante que de sauver quelques octets ici et là.

Pourquoi utilisons-nous int? De combien d'espace est occupé?

L'espace d'un int entier est occupé. Nous utilisons int parce que dans de nombreux cas, cela n'a pas vraiment d'importance. Si, pour une seule valeur, vous utilisez 4 octets au lieu de 1 ou 2, votre utilisateur ne remarquerez probablement pas. Pour certaines plates-formes, la taille importe plus, et vous pouvez utiliser d'autres types de données qui prennent moins de place (char, short, uint8_t, etc.).

Si je comprends bien seulement 1 bit est occupé en mémoire, mais pas toute la valeur int non signée. Est-il correct?

Non, ce n'est pas correct. L'ensemble unsigned int sera exister, même si vous n'utilisez que 8 de ses bits.

44
répondu Eric Finn 2014-07-25 11:09:25

Une assez bonne ressource est champs de bits en C .

La raison fondamentale est de réduire la taille utilisée. Par exemple, si votre écriture:

struct {
    unsigned int is_keyword; 
    unsigned int is_extern; 
    unsigned int is_static;
} flags;

Vous utiliserez au moins 3 * sizeof(unsigned int) ou 12 octets pour représenter 3 petits drapeaux, qui ne devraient avoir besoin que de 3 bits.

Donc, si vous écrivez:

struct {
    unsigned int is_keyword : 1; 
    unsigned int is_extern : 1; 
    unsigned int is_static : 1;
} flags;

Cela utilise le même espace qu'un unsigned int, donc 4 octets. Vous pouvez lancer 32 champs d'un bit dans la structure avant qu'elle n'ait besoin de plus d'espace.

C'est en quelque sorte équivalent au classique Home Brew bit champ:

#define IS_KEYWORD 0x01
#define IS_EXTERN  0x02
#define IS_STATIC  0x04
unsigned int flags;

Mais la syntaxe du champ de bits est plus propre, Comparez:

if (flags.is_keyword)

Contre:

if (flags & IS_KEYWORD)

Et évidemment moins sujettes aux erreurs.

52
répondu rioki 2014-07-24 12:21:09

Un autre endroit où les champs de bits sont communs sont les registres matériels. Si vous avez un registre 32 bits où chaque bit a une certaine signification, vous pouvez le décrire avec élégance avec un bitfield.

Un tel bitfield est intrinsèquement spécifique à la plate-forme. La portabilité n'a pas d'importance dans ce cas.

17
répondu undur_gongor 2015-03-08 18:17:21

Nous utilisons principalement des champs de bits (mais pas exclusivement) pour les structures de drapeau - octets ou mots (ou éventuellement des choses plus grandes) dans lesquels nous essayons d'emballer de minuscules (souvent à 2 États) d'informations (souvent liées).

Dans ces scénarios, les champs de bits sont utilisés car ils modélisent correctement le problème que nous résolvons: ce à quoi nous avons affaire n'est pas vraiment un nombre de 8 bits (ou 16 bits ou 24 bits ou 32 bits), mais plutôt une collection de 8 (ou 16 ou 24 ou 32) liés, mais distincts information.

Les problèmes que nous résolvons en utilisant des champs de bits sont des problèmes où "emballer" l'information a des avantages mesurables et / ou "déballer" l'information n'a pas de pénalité. Par exemple, si vous exposez 1 octet à travers 8 Broches et que les bits de chaque broche passent par leur propre bus qui est déjà imprimé sur la carte afin qu'il mène exactement où il est censé, alors un champ de bits est idéal. L'avantage de "emballer" les données est qu'elles peuvent être envoyées en une seule fois (ce qui est utile si la fréquence du bus est limitée et que notre fonctionnement repose sur la fréquence de son exécution), et la pénalité de "déballage" des données est inexistante (ou existante mais en vaut la peine).

D'autre part, nous n'utilisons pas de champs de bits pour les booléens dans d'autres cas comme le contrôle de flux de programme normal, à cause de la façon dont les architectures informatiques fonctionnent habituellement. La plupart des processeurs courants n'aiment pas aller chercher un bit de la mémoire - ils aiment aller chercher des octets ou des entiers. Ils n'aiment pas non plus traiter les bits - leurs instructions fonctionnent souvent sur des choses plus grandes comme des entiers, des mots, des adresses de mémoire, etc.

Donc, lorsque vous essayez d'opérer sur des bits, c'est à vous ou au compilateur (en fonction de la langue dans laquelle vous écrivez) d'écrire des opérations supplémentaires qui effectuent un masquage de bits et dépouillent la structure de tout sauf les informations sur lesquelles vous voulez réellement fonctionner. S'il n'y a aucun avantage à "emballer" les informations (et dans la plupart des cas, il n'y en a pas), alors utiliser des champs de bits pour les booléens n'introduirait que des frais généraux et du bruit dans votre code.

7
répondu Theodoros Chatzigiannakis 2014-08-14 09:10:12

Pourquoi avons-nous besoin d'utiliser des bits des champs"?

Lorsque vous voulez stocker des données qui peuvent être stockées moins d'octets, ce type de données peut être couplé dans la structure en utilisant des champs de bits. Dans embedded word, quand un monde 32 bits d'un registre a une signification différente pour un mot différent, vous pouvez également utiliser des fichiers de bits pour les rendre plus lisibles.

J'ai trouvé que les champs de bits sont utilisés pour les drapeaux. Maintenant, je suis curieux, est-ce la seule façon dont les champs de bits sont utilisés dans la pratique?

Non ce n'est pas le seul. Vous pouvez l'utiliser d'une autre façon.

Devons-nous utiliser des champs de bits pour économiser de l'espace?

Oui.

Si je comprends bien seulement 1 bit est occupé en mémoire, mais pas toute la valeur int non signée. Est-il correct?

Non. La mémoire seulement peut être occupée dans le multiple d'octet seulement.

6
répondu Jeegar Patel 2014-07-24 12:17:34

Une bonne utilisation serait d'implémenter un morceau pour traduire vers-et depuis-base64 ou toute structure de données non alignée.

struct {
    unsigned int e1:6;
    unsigned int e2:6;
    unsigned int e3:6;
    unsigned int e4:6;
} base64enc; //I don't know if declaring a 4-byte array will have the same effect.

struct {
    unsigned char d1;
    unsigned char d2;
    unsigned char d3;
} base64dec;

union base64chunk {
    struct base64enc enc;
    struct base64dec dec;
};

base64chunk b64c;
//you can assign 3 characters to b64c.enc, and get 4 0-63 codes from b64dec instantly.

Cet exemple est un peu naïf, puisque base64 doit également considérer la terminaison null (c'est-à-dire une chaîne qui n'a pas de longueur l de sorte que l % 3 est 0). Mais fonctionne comme un exemple d'accès à des structures de données non alignées.

Un autre exemple: utiliser cette fonctionnalité pour casser un en-tête de paquet TCP dans ses composants (ou un autre en-tête de paquet de protocole réseau je veux discuter), bien que ce soit un exemple plus avancé et moins d'utilisateur final. En général: ceci est utile en ce qui concerne les internes du PC, donc, les pilotes, un système de codage.

Un autre exemple: analyser un nombre float.

struct _FP32 {
    unsigned int sign:1;
    unsigned int exponent:8;
    unsigned int mantissa:23;
}

union FP32_t {
    _FP32 parts;
    float number;
}

(avertissement: Je ne connais pas le nom du fichier / Nom du type où cela est appliqué, mais en C cela est déclaré dans un en-tête; Je ne sais pas comment cela peut-il être fait pour les flaots 64 bits puisque la mantisse doit avoir 52 bits et-dans une cible 32 bits - ints ont 32 bits bit).

Conclusion: Comme le montrent le concept et ces exemples, il s'agit d'une fonctionnalité rarement utilisée car elle est principalement utilisée à des fins internes, et non pour les logiciels au jour le jour.

5
répondu Luis Masuelli 2014-08-14 14:58:41

Pour répondre à la question initiale " quand utiliser les champs de bits en C?"... selon le livre "Write Portable Code" de Brian Hook (ISBN 1-59327-056-9, j'ai lu L'édition allemande ISBN 3-937514-19-8)et à l'expérience personnelle:

N'utilisez jamais l'idiome bitfield du langage C mais faites-le vous-même.

Beaucoup de détails d'implémentation sont spécifiques au compilateur, en particulier en combinaison avec des unions et les choses ne sont pas garanties sur différents compilateurs et différentes endianess. Si il n'y a qu'une petite chance que votre code soit portable et soit compilé pour différentes architectures et/ou avec différents compilateurs, ne l'utilisez pas.

Nous avons eu ce cas lors du portage de code de little endian micro controller avec un compilateur propriétaire vers un autre big endian micro controller avec GCC et ce n'était pas amusant. :-/

Voici comment j'utilise flags (host byte order ;-) ) depuis lors:

# define SOME_FLAG        (1 << 0)
# define SOME_OTHER_FLAG  (1 << 1)
# define AND_ANOTHER_FLAG (1 << 2)

/* test flag */
if ( someint & SOME_FLAG ) {
    /* do this */
}

/* set flag */
someint |= SOME_FLAG;

/* clear flag */
someint &= ~SOME_FLAG;

Pas besoin d'une union avec le type int et une structure bitfield alors. Si vous lisez beaucoup de code intégré ces modèles test, set et clear deviendront communs et vous les repérez facilement dans votre code.

5
répondu LeSpocky 2015-02-27 14:57:11

Les champs de bits peuvent être utilisés pour économiser de l'espace mémoire (mais l'utilisation de champs de bits à cette fin est rare). Il est utilisé là où il y a une contrainte de mémoire. par exemple) lors de la programmation dans les systèmes embarqués.

Mais cela ne devrait être utilisé que si cela est extrêmement nécessaire.

Parce que nous ne pouvons pas avoir l'adresse d'un champ de bits. Donc adresse opérateur & ne peut pas être utilisé avec eux.

3
répondu rohit 2016-02-07 05:46:44

Vous pouvez les utiliser pour augmenter le nombre de types non signés qui enveloppent. Ordinaire, vous n'auriez que des pouvoirs de 8,16,32,64... , mais vous pouvez avoir tous les pouvoirs avec des champs de bits.

struct a
{
    unsigned int b : 3 ;
} ;

struct a w = { 0 } ;

while( 1 )
{
    printf("%u\n" , w.b++ ) ;
    getchar() ;
}
2
répondu this 2014-07-24 12:27:07

Pour répondre aux parties de la question, personne d'autre n'a répondu:

Ints pas de Shorts

La raison d'utiliser ints plutôt que shorts etc est que dans la plupart des cas, aucun espace ne sera économisé en le faisant.

Les ordinateurs modernes ont une architecture 32 ou 64 bits et que 32 ou 64 bits seront nécessaires même si vous utilisez un type de stockage plus petit comme un court.

Les types plus petits ne sont utiles que pour économiser de la mémoire si vous pouvez les emballer ensemble (par exemple, un tableau court peut utilisez moins de mémoire qu'un tableau int car les shorts peuvent être emballés ensemble plus serré dans le tableau). Dans la plupart des cas, lorsque vous utilisez des champs bitfields, ce n'est pas le cas.

D'Autres utilisations

Les champs de bits sont les plus couramment utilisés pour les drapeaux, mais il y a d'autres choses pour lesquelles ils sont utilisés. Par exemple, une façon de représenter un échiquier utilisé dans de nombreux algorithmes d'échecs est d'utiliser un entier 64 bits pour représenter le tableau (8 * 8 pixels) et de définir des drapeaux dans cet entier pour donner la position de tous les blancs pion. Un autre entier montre tous les pions noirs, etc.

2
répondu Tim B 2014-07-24 13:39:54

Les champs de bits sont beaucoup plus compacts et c'est un avantage.

Mais n'oubliez pas que les structures emballées sont plus lentes que les structures normales. Ils sont également plus difficile à construire, puisque le programmeur doit définir le nombre de bits à utiliser pour chaque champ.C'est un inconvénient

1
répondu AVI 2015-12-24 03:16:54

Pour utiliser l'espace mémoire, nous pouvons utiliser des champs de bits.

Pour autant que je sache dans la programmation du monde réel si nous avons besoin, nous pouvons utiliser des booléens au lieu de le déclarer comme des entiers, puis faire un champ de bits.

0
répondu Srinivasa Lakkaraju 2014-07-24 12:14:19

Si ce sont aussi des valeurs que nous utilisons souvent, non seulement nous économisons de l'espace, mais nous pouvons également gagner en performance car nous n'avons pas besoin de polluer les caches. Cependant, la mise en cache est également le danger d'utiliser des champs de bits car les lectures et les Écritures simultanées sur différents bits provoqueront une course de données et les mises à jour de bits complètement séparés pourraient écraser les nouvelles valeurs avec les anciennes valeurs..

0
répondu Ghaban 2014-07-24 12:23:33

Pourquoi utilisons-nous int? De combien d'espace est occupé?

Une réponse à cette question que je n'ai vu mentionnée dans aucune des autres réponses, est que la norme C garantit le support de int. Plus précisément:

Un champ de bits doit avoir un type qui est une version qualifiée ou non qualifiée de _Bool, signed int, unsigned int ou un autre type défini par l'implémentation.

Il est courant que les compilateurs autorisent des types de champs de bits supplémentaires, mais n'est pas requise. Si vous êtes vraiment préoccupé par la portabilité, int est le meilleur choix.

0
répondu zmb 2014-12-10 20:41:55