52 votes

Manipulation de champs de bits en C

Le problème classique de test et de configuration de bits en un nombre entier dans C est peut-être le plus commun de niveau intermédiaire compétences en programmation. Vous jeu et le tester avec de simples masques de bits tels que

unsigned int mask = 1<<11;

if (value & mask) {....} // Test for the bit
value |= mask;    // set the bit
value &= ~mask;   // clear the bit

Un blog intéressant post affirme que c'est source d'erreurs, difficile à maintenir, et des mauvaises pratiques. Le langage C fournit elle-même peu d'accès au niveau de ce qui est typesafe et portable:

typedef unsigned int boolean_t;
#define FALSE 0
#define TRUE !FALSE
typedef union {
        struct {
                boolean_t user:1;
                boolean_t zero:1;
                boolean_t force:1;
                int :28;                /* unused */
                boolean_t compat:1;     /* bit 31 */
        };
        int raw;
} flags_t;

int
create_object(flags_t flags)
{
        boolean_t is_compat = flags.compat;

        if (is_compat)
                flags.force = FALSE;

        if (flags.force) {
                [...]
        }
        [...]
}

Mais ce qui me fait grincer des dents.

L'argument intéressant ma collègue de travail et j'ai eu à ce sujet est encore en suspens. Les deux styles de travail, et je maintiens le classique masque méthode est facile, sûr et clair. Mon collègue est d'accord, il est courant et facile, mais le champ de bits de l'union méthode vaut le supplément de quelques lignes pour le rendre portable et plus sûr.

Est-il plus d'arguments de chaque côté? En particulier est-il un échec possible, peut-être avec l'endianness, que le masque de bits méthode peut manquer, mais où la structure de la méthode est sans danger?

46voto

Matthew Flaschen Points 131723

Les champs de bits ne sont pas aussi portables que vous le pensez, car "C ne donne aucune garantie quant à la commande des champs dans les mots machine" ( Le livre C )

En ignorant cela, utilisée correctement , l'une ou l'autre méthode est sans danger. Les deux méthodes permettent également un accès symbolique aux variables intégrales. Vous pouvez affirmer que la méthode bitfield est plus facile à écrire, mais cela signifie également plus de code à réviser.

31voto

plinth Points 26817

Si le problème est que l'établissement et la compensation de bits est sujette aux erreurs, puis la bonne chose à faire est d'écrire des fonctions ou macros assurez-vous que vous le faites à droite.

// off the top of my head
#define SET_BIT(val, bitIndex) val |= (1 << bitIndex)
#define CLEAR_BIT(val, bitIndex) val &= ~(1 << bitIndex)
#define TOGGLE_BIT(val, bitIndex) val ^= (1 << bitIndex)
#define BIT_IS_SET(val, bitIndex) (val & (1 << bitIndex))

Ce qui rend votre code lisible si vous n'avez pas l'esprit que val a à être une lvalue, sauf pour BIT_IS_SET. Si cela ne vous rend pas heureux, alors vous prenez affectation, parenthesize et de les utiliser comme val = SET_BIT(val, someIndex); qui sera l'équivalent.

Vraiment, la réponse est à considérer découplage de la ce que vous voulez de la façon dont vous voulez le faire.

24voto

Norman Ramsey Points 115730

Les champs de bits sont excellents et faciles à lire, mais malheureusement, le langage C ne spécifie pas la disposition des champs de bits en mémoire , ce qui signifie qu'ils sont essentiellement inutiles pour traiter des données condensées dans des formats sur disque ou des protocoles filaires. Si vous me le demandez, cette décision était une erreur de conception dans C — Ritchie aurait pu choisir une commande et s'y tenir.

20voto

Doug T. Points 33360

Vous devez penser à ce sujet dans le point de vue d'un écrivain -- connaître votre public. Donc, il ya un couple de "publics" à prendre en compte.

D'abord il y a le classique programmeur C, qui ont bitmasked toute leur vie et pourrait le faire dans leur sommeil.

Deuxième il y a le newb, qui n'a aucune idée de ce que tout cela,|, & stuff est. Ils ont été de programmation php à leur travail, et maintenant ils travaillent pour vous. (Je dis cela comme un newb qui n'php)

Si vous écrivez pour satisfaire à la première audience (c'est-masque-tous-journée), vous allez les rendre très heureux, et ils vont être en mesure de maintenir le code, les yeux bandés. Cependant, le newb aurez probablement besoin de surmonter une grande courbe d'apprentissage avant qu'ils ne soient en mesure de maintenir votre code. Ils auront besoin d'apprendre sur les opérateurs binaires, la façon dont vous utilisez ces opérations à définir/effacer les bits, etc. Vous êtes presque certainement allez avoir des bugs introduits par la newb comme il/elle toutes les astuces nécessaires pour obtenir que cela fonctionne.

D'autre part, si vous écrivez à satisfaire à la seconde audience, les newbs aurez un temps plus facile de maintenir le code. Ils ont un temps plus facile de groking

 flags.force = 0;

que

 flags &= 0xFFFFFFFE;

et la première audience aura juste grognon, mais il est difficile d'imaginer qu'ils ne serait pas en mesure d'analyser et de maintenir la nouvelle syntaxe. C'est juste beaucoup plus difficile à visser. Il n'y aura pas de nouveaux bugs, parce que le newb sera plus facile de maintenir le code. Il vous suffit de faire des conférences sur comment "de retour dans ma journée vous avez besoin d'une main ferme et d'une aiguille magnétisée pour définir les bits... nous n'avons même pas de masques de bits!" (merci XKCD).

Donc, je voudrais vous recommandons vivement d'utiliser les champs sur les masques de bits de newb-sûr de votre code.

14voto

Juliano Points 13802

L'union d'utilisation a un comportement non défini selon la norme ANSI C la norme, et donc, ne doit pas être utilisé (ou au moins ne pas être considéré comme portable).

À partir de la norme ISO/IEC 9899:1999 (C99) standard:

Annexe J - Problèmes De Portabilité:

1 Les éléments suivants sont non spécifié:

- La valeur des octets de remplissage lors de stocker des valeurs dans des structures ou des syndicats (6.2.6.1).

- La valeur d'un membre de l'union autres que le dernier stockées dans (6.2.6.1).

6.2.6.1 - Concepts de Langage de Représentation des Types Généraux:

6 Lorsqu'une valeur est stockée dans un objet de structure ou le type d'union, y compris dans un membre objet, les octets de la représentation des objets qui correspondent à aucun des octets de remplissage prendre non spécifié de valeurs.[42]) La valeur d'une structure ou d'une union de l'objet n'est jamais un piège la représentation, même si la valeur d'un membre de la structure ou de l'union de l'objet peut être un piège de la représentation.

7 Lorsqu'une valeur est stockée dans un membre d'un objet de type union, les octets de l'objet la représentation qui ne correspond pas à ce membre, mais ne correspondent à d'autres membres prendre de quelconques valeurs.

Donc, si vous voulez garder le champ de bits ↔ entier de la correspondance, et de garder la portabilité, je vous suggère fortement d'utiliser la bitmasking méthode, que, contrairement à ce lié billet de blog, c'est pas une mauvaise pratique.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X