Comment définir, effacer et basculer un seul bit?


Réponses:


3600

Mettre un peu

Utilisez l'opérateur OR au niveau du bit ( |) pour définir un bit.

number |= 1UL << n;

Cela mettra le ne bit de number. ndevrait être zéro, si vous voulez régler le 1bit st et ainsi de suite jusqu'à n-1, si vous voulez régler le nbit bit.

Utilisez 1ULLsi numberest plus large que unsigned long; la promotion de 1UL << nne se produit qu'après avoir évalué 1UL << noù le comportement indéfini se déplace de plus de la largeur de a long. La même chose s'applique à tous les autres exemples.

Effacer un peu

Utilisez l'opérateur AND au niveau du bit ( &) pour effacer un peu.

number &= ~(1UL << n);

Cela effacera le ne bit de number. Vous devez inverser la chaîne de bits avec l'opérateur NOT au niveau du bit ( ~), puis AND.

Basculer un peu

L'opérateur XOR ( ^) peut être utilisé pour basculer un peu.

number ^= 1UL << n;

Cela fera basculer le ne bit de number.

Vérifier un peu

Vous ne l'avez pas demandé, mais je pourrais aussi bien l'ajouter.

Pour vérifier un peu, déplacez le nombre n vers la droite, puis au niveau du bit ET:

bit = (number >> n) & 1U;

Cela mettra la valeur du ne bit de numberdans la variable bit.

Modification de la n ième bit à x

La définition du nth bit sur 1ou 0peut être obtenue avec les éléments suivants sur une implémentation C ++ complémentaire à 2:

number ^= (-x ^ number) & (1UL << n);

Le bit nsera défini si xest 1, et effacé si xest 0. Si xa une autre valeur, vous obtenez des ordures. x = !!xla booléenne à 0 ou 1.

Pour rendre cela indépendant du comportement de négation du complément à 2 (où -1tous les bits sont définis, contrairement à une implémentation C ++ de complément ou de signe / magnitude), utilisez la négation non signée.

number ^= (-(unsigned long)x ^ number) & (1UL << n);

ou

unsigned long newbit = !!x;    // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);

C'est généralement une bonne idée d'utiliser des types non signés pour la manipulation de bits portables.

ou

number = (number & ~(1UL << n)) | (x << n);

(number & ~(1UL << n))effacera le nbit e et (x << n)mettra le nbit bit sur x.

C'est aussi généralement une bonne idée de ne pas copier / coller de code en général et tant de gens utilisent des macros de préprocesseur (comme la réponse du wiki communautaire plus bas ) ou une sorte d'encapsulation.


128
Je voudrais noter que sur les plates-formes qui prennent en charge nativement le bit set / clear (par exemple, les microcontrôleurs AVR), les compilateurs traduiront souvent 'myByte | = (1 << x)' en bit set natif / clear instructions chaque fois que x est une constante, ex: (1 << 5), ou const unsigned x = 5.
Aaron

52
bit = nombre & (1 << x); ne mettra pas la valeur du bit x dans bit à moins que bit ait le type _Bool (<stdbool.h>). Sinon, bit = !! (nombre & (1 << x)); sera ..
Chris Young

23
pourquoi ne changez-vous pas le dernierbit = (number >> x) & 1
aaronman

42
1est un intlittéral, qui est signé. Donc, toutes les opérations ici fonctionnent sur des numéros signés, ce qui n'est pas bien défini par les normes. Les normes ne garantissent pas le complément à deux ou le décalage arithmétique, il est donc préférable de l'utiliser 1U.
Siyuan Ren

50
Je préfère number = number & ~(1 << n) | (x << n);changer le nième bit en x.
jiasli

459

Utilisation de la bibliothèque standard C ++: std::bitset<N>.

Ou la Boost version: boost::dynamic_bitset.

Il n'est pas nécessaire de rouler le vôtre:

#include <bitset>
#include <iostream>

int main()
{
    std::bitset<5> x;

    x[1] = 1;
    x[2] = 0;
    // Note x[0-4]  valid

    std::cout << x << std::endl;
}

[Alpha:] > ./a.out
00010

La version Boost permet un ensemble de bits de taille d'exécution par rapport à un ensemble de bits de taille de compilation standard de bibliothèque .


34
+1. Non pas que std :: bitset soit utilisable à partir de "C", mais comme l'auteur a tagué sa question avec "C ++", AFAIK, votre réponse est la meilleure ici ... std :: vector <bool> est une autre façon, si on connaît ses avantages et ses inconvénients
paercebal

23
@andrewdotnich: le vecteur <bool> est (malheureusement) une spécialisation qui stocke les valeurs sous forme de bits. Voir gotw.ca/publications/mill09.htm pour plus d'informations ...
Niklas

71
Peut-être que personne ne l'a mentionné parce que cela était étiqueté comme étant intégré. Dans la plupart des systèmes embarqués, vous évitez les STL comme la peste. Et le support de boost est probablement un oiseau très rare à repérer parmi la plupart des compilateurs intégrés.
Lundin

17
@Martin C'est très vrai. Outre les tueurs de performances spécifiques comme STL et les modèles, de nombreux systèmes embarqués évitent même complètement les bibliothèques standard entières, car ils sont si difficiles à vérifier. La plupart de la branche embarquée adopte des normes comme MISRA, qui nécessitent des outils d'analyse de code statique (tous les professionnels du logiciel devraient utiliser de tels outils entre autres, pas seulement des gens embarqués). Généralement, les gens ont mieux à faire que d'exécuter une analyse statique à travers toute la bibliothèque standard - si son code source est même disponible pour eux sur le compilateur spécifique.
Lundin

37
@Lundin: Vos déclarations sont excessivement larges (donc inutile de discuter). Je suis sûr que je peux trouver des situations où elles sont vraies. Cela ne change pas mon point de départ. Ces deux classes sont parfaitement adaptées à une utilisation dans des systèmes embarqués (et je sais pertinemment qu'elles sont utilisées). Votre point initial sur le fait que STL / Boost n'est pas utilisé sur les systèmes embarqués est également faux. Je suis sûr qu'il existe des systèmes qui ne les utilisent pas et même les systèmes qui les utilisent, ils sont utilisés judicieusement, mais dire qu'ils ne sont pas utilisés n'est tout simplement pas correct (car il existe des systèmes où ils sont utilisés).
Martin York

248

L'autre option consiste à utiliser des champs de bits:

struct bits {
    unsigned int a:1;
    unsigned int b:1;
    unsigned int c:1;
};

struct bits mybits;

définit un champ de 3 bits (en fait, il s'agit de trois champs de 1 bit). Les opérations sur les bits deviennent maintenant un peu (haha) plus simples:

Pour régler ou effacer un peu:

mybits.b = 1;
mybits.c = 0;

Pour basculer un peu:

mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1;  /* all work */

Vérification un peu:

if (mybits.c)  //if mybits.c is non zero the next line below will execute

Cela ne fonctionne qu'avec des champs de bits de taille fixe. Sinon, vous devez recourir aux techniques de twiddling décrites dans les articles précédents.


68
J'ai toujours trouvé que l'utilisation des champs de bits était une mauvaise idée. Vous n'avez aucun contrôle sur l'ordre dans lequel les bits sont alloués (du haut ou du bas), ce qui rend impossible la sérialisation de la valeur de manière stable / portable, sauf bit par bit. Il est également impossible de mélanger l'arithmétique des bits de bricolage avec les champs de bits, par exemple en créant un masque qui teste plusieurs bits à la fois. Vous pouvez bien sûr utiliser && et espérer que le compilateur l'optimisera correctement ...
R .. GitHub STOP HELPING ICE

34
Les champs de bits sont mauvais à bien des égards, je pourrais presque écrire un livre à ce sujet. En fait, j'ai presque dû le faire pour un programme de terrain peu qui nécessitait la conformité MISRA-C. MISRA-C applique tous les comportements définis par l'implémentation à documenter, j'ai donc fini par écrire un essai sur tout ce qui peut mal se passer dans les champs de bits. Ordre des bits, endianess, bits de remplissage, octets de remplissage, divers autres problèmes d'alignement, conversions de types implicites et explicites vers et depuis un champ de bits, UB si int n'est pas utilisé, etc. Utilisez plutôt des opérateurs au niveau du bit pour moins de bogues et du code portable. Les champs de bits sont complètement redondants.
Lundin

44
Comme la plupart des fonctionnalités linguistiques, les champs de bits peuvent être utilisés correctement ou ils peuvent être utilisés abusivement. Si vous devez regrouper plusieurs petites valeurs dans un seul entier, les champs de bits peuvent être très utiles. D'un autre côté, si vous commencez à faire des hypothèses sur la façon dont les champs de bits sont mappés à l'intent contenant réel, vous demandez simplement des problèmes.
Ferruccio

4
@endolith: Ce ne serait pas une bonne idée. Vous pourriez le faire fonctionner, mais il ne serait pas nécessairement portable sur un autre processeur, sur un autre compilateur ou même sur la prochaine version du même compilateur.
Ferruccio

3
@Yasky et Ferruccio obtenant des réponses différentes à un sizeof () pour cette approche devraient illustrer les problèmes de compatibilité non seulement entre les compilateurs mais aussi avec le matériel. Nous nous trompons parfois que nous avons résolu ces problèmes avec des langages ou des temps d'exécution définis, mais cela se résume vraiment à "cela fonctionnera-t-il sur ma machine?". Vous, les gars intégrés, avez mon respect (et mes sympathies).
Kelly S. French

181

J'utilise des macros définies dans un fichier d'en-tête pour gérer les bits définis et effacer:

/* a=target variable, b=bit number to act upon 0-n */
#define BIT_SET(a,b) ((a) |= (1ULL<<(b)))
#define BIT_CLEAR(a,b) ((a) &= ~(1ULL<<(b)))
#define BIT_FLIP(a,b) ((a) ^= (1ULL<<(b)))
#define BIT_CHECK(a,b) (!!((a) & (1ULL<<(b))))        // '!!' to make sure this returns 0 or 1

/* x=target variable, y=mask */
#define BITMASK_SET(x,y) ((x) |= (y))
#define BITMASK_CLEAR(x,y) ((x) &= (~(y)))
#define BITMASK_FLIP(x,y) ((x) ^= (y))
#define BITMASK_CHECK_ALL(x,y) (((x) & (y)) == (y))   // warning: evaluates y twice
#define BITMASK_CHECK_ANY(x,y) ((x) & (y))

17
Euh, je me rends compte que c'est un article de 5 ans mais il n'y a pas de duplication d'argument dans aucune de ces macros, Dan
Robert Kelly

11
BITMASK_CHECK(x,y) ((x) & (y))doit être ((x) & (y)) == (y)sinon il retourne un résultat incorrect sur le masque multibit (ex. 5vs 3) / * Bonjour à tous les fossoyeurs:) * /
brigadir

7
1devrait être (uintmax_t)1ou similaire dans le cas où quelqu'un essaie d'utiliser ces macros sur un longtype plus grand
MM

2
BITMASK_CHECK_ALL(x,y)peut être implémenté comme!~((~(y))|(x))
Handy999

3
@ Handy999 Il est un peu plus facile de voir pourquoi cela fonctionne après avoir appliqué la loi De Morgan et réorganisé pour obtenir!(~(x) & (y))
Tavian Barnes

114

Il vaut parfois la peine d'utiliser un enumpour nommer les bits:

enum ThingFlags = {
  ThingMask  = 0x0000,
  ThingFlag0 = 1 << 0,
  ThingFlag1 = 1 << 1,
  ThingError = 1 << 8,
}

Ensuite, utilisez les noms plus tard. C'est à dire écrire

thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}

pour régler, effacer et tester. De cette façon, vous masquez les nombres magiques du reste de votre code.

À part cela, j'approuve la solution de Jeremy.


1
Alternativement, vous pouvez créer une clearbits()fonction au lieu de &= ~. Pourquoi utilisez-vous une énumération pour cela? Je pensais que c'était pour créer un tas de variables uniques avec une valeur arbitraire cachée, mais vous attribuez une valeur définie à chacune. Alors, quel est l'avantage par rapport à leur simple définition de variables?
endolith

4
@endolith: L'utilisation de enums pour des ensembles de constantes liées remonte très loin dans la programmation c. Je soupçonne qu'avec les compilateurs modernes, le seul avantage par rapport à const shortquoi que ce soit, c'est qu'ils sont explicitement regroupés. Et lorsque vous les voulez pour autre chose que des masques de bit, vous obtenez la numérotation automatique. En c ++ bien sûr, ils forment également des types distincts, ce qui vous permet de vérifier les erreurs statiques en plus.
dmckee --- chaton ex-modérateur

Vous entrerez dans des constantes d'énumération non définies si vous ne définissez pas de constante pour chacune des valeurs possibles des bits. Quelle est la enum ThingFlagsvaleur ThingError|ThingFlag1, par exemple?
Luis Colorado

6
Si vous utilisez cette méthode, n'oubliez pas que les constantes enum sont toujours de type signé int. Cela peut provoquer toutes sortes de bogues subtils en raison de la promotion implicite d'entiers ou d'opérations au niveau du bit sur les types signés. thingstate = ThingFlag1 >> 1invoquera par exemple un comportement défini par l'implémentation. thingstate = (ThingFlag1 >> x) << ypeut invoquer un comportement non défini. Etc. Pour être sûr, toujours cast dans un type non signé.
Lundin

1
@Lundin: Depuis C ++ 11, vous pouvez définir le type sous-jacent d'une énumération, par exemple: enum My16Bits: unsigned short { ... };
Aiken Drum

47

Du bitops.h de snip-c.zip:

/*
**  Bit set, clear, and test operations
**
**  public domain snippet by Bob Stout
*/

typedef enum {ERROR = -1, FALSE, TRUE} LOGICAL;

#define BOOL(x) (!(!(x)))

#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))

OK, analysons les choses ...

L'expression courante avec laquelle vous semblez avoir des problèmes avec tous ces éléments est "(1L << (posn))". Tout cela ne fait que créer un masque avec un seul bit et qui fonctionnera avec n'importe quel type entier. L'argument "posn" spécifie la position où vous voulez que le bit. Si posn == 0, alors cette expression sera évaluée à:

0000 0000 0000 0000 0000 0000 0000 0001 binary.

Si posn == 8, il évaluera:

0000 0000 0000 0000 0000 0001 0000 0000 binary.

En d'autres termes, il crée simplement un champ de 0 avec un 1 à la position spécifiée. La seule partie délicate est dans la macro BitClr () où nous devons définir un seul bit 0 dans un champ de 1. Ceci est accompli en utilisant le complément 1 de la même expression que celle indiquée par l'opérateur tilde (~).

Une fois le masque créé, il est appliqué à l'argument comme vous le suggérez, en utilisant les opérateurs au niveau du bit et (&), ou (|) et xor (^). Puisque le masque est de type long, les macros fonctionneront tout aussi bien sur les caractères, les courts, les int ou les longs.

L'essentiel est qu'il s'agit d'une solution générale à toute une classe de problèmes. Il est bien sûr possible et même approprié de réécrire l'équivalent de n'importe laquelle de ces macros avec des valeurs de masque explicites à chaque fois que vous en avez besoin, mais pourquoi le faire? N'oubliez pas que la substitution de macro se produit dans le préprocesseur et que le code généré reflétera le fait que les valeurs sont considérées comme constantes par le compilateur - c'est-à-dire qu'il est tout aussi efficace d'utiliser les macros généralisées que de "réinventer la roue" à chaque fois que vous en avez besoin. faire de la manipulation de bits.

Pas convaincu? Voici un code de test - j'ai utilisé Watcom C avec une optimisation complète et sans utiliser _cdecl pour que le démontage résultant soit aussi propre que possible:

---- [TEST.C] ----------------------------------------- -----------------------

#define BOOL(x) (!(!(x)))

#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))

int bitmanip(int word)
{
      word = BitSet(word, 2);
      word = BitSet(word, 7);
      word = BitClr(word, 3);
      word = BitFlp(word, 9);
      return word;
}

---- [TEST.OUT (démonté)] -------------------------------------- ---------

Module: C:\BINK\tst.c
Group: 'DGROUP' CONST,CONST2,_DATA,_BSS

Segment: _TEXT  BYTE   00000008 bytes  
 0000  0c 84             bitmanip_       or      al,84H    ; set bits 2 and 7
 0002  80 f4 02                          xor     ah,02H    ; flip bit 9 of EAX (bit 1 of AH)
 0005  24 f7                             and     al,0f7H
 0007  c3                                ret     

No disassembly errors

---- [finis] ------------------------------------------- ----------------------


3
2 choses à ce sujet: (1) en parcourant vos macros, certains peuvent croire à tort que les macros définissent / effacent / inversent les bits dans l'argument, mais il n'y a pas d'affectation; (2) votre test.c n'est pas terminé; Je soupçonne que si vous couriez plus de cas, vous trouveriez un problème (exercice de lecture)
Dan

19
-1 C'est juste de l'obscurcissement bizarre. Ne réinventez jamais le langage C en cachant la syntaxe du langage derrière les macros, c'est une très mauvaise pratique. Ensuite, quelques bizarreries: d'abord, 1L est signé, ce qui signifie que toutes les opérations sur les bits seront effectuées sur un type signé. Tout ce qui est passé à ces macros sera renvoyé comme signé longtemps. Pas bon. Deuxièmement, cela fonctionnera de manière très inefficace sur les CPU plus petits car il s'applique longtemps lorsque les opérations auraient pu être au niveau int. Troisièmement, les macros fonctionnelles sont à l'origine de tous les maux: vous n'avez aucune sécurité de type. En outre, le commentaire précédent sur aucune affectation est très valide.
Lundin

2
Cela échouera si argc'est le cas long long. 1Ldoit être le type le plus large possible, donc (uintmax_t)1. (Vous pourriez vous en tirer 1ull)
MM

Avez-vous optimisé la taille du code? Sur les processeurs Intel traditionnels, vous obtiendrez des blocages de registre partiel lorsque vous lirez AX ou EAX après le retour de cette fonction, car elle écrit les composants 8 bits d'EAX. (C'est très bien sur les processeurs AMD, ou d'autres qui ne renomment pas les registres partiels séparément du registre complet. Haswell / Skylake ne renomment pas AL séparément, mais ils renomment AH. ).
Peter Cordes

37

Utilisez les opérateurs au niveau du bit: & |

Pour définir le dernier bit dans 000b:

foo = foo | 001b

Pour archiver le dernier bit foo:

if ( foo & 001b ) ....

Pour effacer le dernier bit dans foo:

foo = foo & 110b

J'ai utilisé XXXbpour plus de clarté. Vous travaillerez probablement avec la représentation HEX, selon la structure de données dans laquelle vous empaquetez les bits.


7
Il n'y a pas de notation binaire en C. Les constantes entières binaires sont une extension non standard.
Lundin

Utilisez XOR pour basculer un peu:foo = foo ^ MY_MASK
Peter L

Utilisez PAS pour inverser un masque pour faire clair:foo = foo & ~MY_MASK
Peter L

32

Pour le débutant, je voudrais expliquer un peu plus avec un exemple:

Exemple:

value is 0x55;
bitnum : 3rd.

L' &opérateur est utilisé vérifier le bit:

0101 0101
&
0000 1000
___________
0000 0000 (mean 0: False). It will work fine if the third bit is 1 (then the answer will be True)

Basculer ou retourner:

0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)

| opérateur: définir le bit

0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)

26

Voici ma macro arithmétique de bits préférée, qui fonctionne pour tout type de tableau d'entiers non signés allant unsigned charjusqu'à size_t(qui est le plus grand type qui devrait être efficace pour travailler):

#define BITOP(a,b,op) \
 ((a)[(size_t)(b)/(8*sizeof *(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof *(a)))))

Pour régler un peu:

BITOP(array, bit, |=);

Pour effacer un peu:

BITOP(array, bit, &=~);

Pour basculer un peu:

BITOP(array, bit, ^=);

Pour tester un peu:

if (BITOP(array, bit, &)) ...

etc.


5
C'est bon à lire, mais il faut être conscient des effets secondaires possibles. L'utilisation BITOP(array, bit++, |=);en boucle ne fera probablement pas ce que l'appelant veut.
foraidt

En effet. =) Une variante que vous préféreriez peut-être est de le séparer en 2 macros, 1 pour adresser l'élément de tableau et l'autre pour déplacer le bit en place, ala BITCELL(a,b) |= BITMASK(a,b);(les deux prennent acomme argument pour déterminer la taille, mais ce dernier n'évaluerait jamais adepuis il n'apparaît qu'en sizeof).
R .. GitHub STOP HELPING ICE

1
@R .. Cette réponse est vraiment ancienne, mais je préférerais probablement une fonction à une macro dans ce cas.
PC Luddite

Mineure: la 3e (size_t)distribution semble être là uniquement pour assurer des mathématiques non signées avec %. Pourrait (unsigned)là.
chux

Le (size_t)(b)/(8*sizeof *(a))pourrait inutilement rétrécir bavant la division. Seul problème avec les tableaux de bits très volumineux. Encore une macro intéressante.
chux

25

Comme cela est étiqueté «intégré», je suppose que vous utilisez un microcontrôleur. Toutes les suggestions ci-dessus sont valables et fonctionnent (lecture-modification-écriture, unions, structures, etc.).

Cependant, lors d'un débogage basé sur un oscilloscope, j'ai été étonné de constater que ces méthodes ont un surcoût considérable dans les cycles CPU par rapport à l'écriture d'une valeur directement dans les registres PORTnSET / PORTnCLEAR du micro, ce qui fait une réelle différence là où il y a des boucles serrées / hautes broches de basculement de l'ISR de fréquence.

Pour ceux qui ne sont pas familiers: Dans mon exemple, le micro a un registre d'état de broche général PORTn qui reflète les broches de sortie, ce qui fait que PORTn | = BIT_TO_SET entraîne une lecture-modification-écriture dans ce registre. Cependant, les registres PORTnSET / PORTnCLEAR prennent un «1» pour signifier «veuillez mettre ce bit à 1» (SET) ou «veuillez mettre ce bit à zéro» (CLEAR) et un «0» pour signifier «laisser la broche tranquille». ainsi, vous vous retrouvez avec deux adresses de port selon que vous définissez ou effacez le bit (pas toujours pratique) mais une réaction beaucoup plus rapide et un code assemblé plus petit.


Micro était Coldfire MCF52259, utilisant C dans Codewarrior. Regarder le désassembleur / asm est un exercice utile car il montre toutes les étapes que le CPU doit parcourir pour effectuer même les opérations les plus élémentaires. <br> Nous avons également repéré d'autres instructions de contournement du processeur dans les boucles à temps critique - contraindre une variable en faisant var% = max_val coûte une pile de cycles CPU à chaque fois, alors que si (var> max_val) var- = max_val utilise uniquement quelques instructions. <br> Un bon guide pour quelques astuces supplémentaires est ici: codeproject.com/Articles/6154/…
John U

Plus important encore, les registres d'E / S mappés en mémoire auxiliaires fournissent un mécanisme pour les mises à jour atomiques. La lecture / modification / écriture peut très mal se passer si la séquence est interrompue.
Ben Voigt

1
Gardez à l'esprit que tous les registres de port seront définis en tant que volatileet par conséquent le compilateur n'est pas en mesure d'effectuer des optimisations sur le code impliquant ces registres. Par conséquent, il est recommandé de démonter un tel code et de voir comment il s'est avéré au niveau de l'assembleur.
Lundin

24

L'approche par champ de bits présente d'autres avantages dans l'arène intégrée. Vous pouvez définir une structure qui mappe directement sur les bits d'un registre matériel particulier.

struct HwRegister {
    unsigned int errorFlag:1;  // one-bit flag field
    unsigned int Mode:3;       // three-bit mode field
    unsigned int StatusCode:4;  // four-bit status code
};

struct HwRegister CR3342_AReg;

Vous devez être conscient de l'ordre de compression des bits - je pense que c'est MSB d'abord, mais cela peut dépendre de l'implémentation. Vérifiez également comment vos gestionnaires de compilateur gèrent les champs en franchissant les limites d'octets.

Vous pouvez ensuite lire, écrire, tester les valeurs individuelles comme précédemment.


2
Presque tout ce qui concerne les champs binaires est défini par l'implémentation. Même si vous parvenez à trouver tous les détails sur la façon dont votre compilateur les implémente, les utiliser dans votre code le rendra certainement non portable.
Lundin

1
@Lundin - Certes, mais le bidouillage du système embarqué (en particulier dans les registres matériels, ce à quoi ma réponse se rapporte) ne sera jamais utile de toute façon.
Roddy

1
Pas entre des processeurs entièrement différents peut-être. Mais vous voudrez probablement qu'il soit portable entre les compilateurs et entre les différents projets. Et il y a beaucoup de "bit fiddling" intégré qui n'est pas du tout lié au matériel, comme l'encodage / décodage du protocole de données.
Lundin

... et si vous avez l'habitude d'utiliser des champs binaires pour faire de la programmation intégrée, vous constaterez que votre code X86 s'exécute plus rapidement et plus léger aussi. Pas dans des benchmarks simples où vous avez toute la machine pour écraser le benchmark, mais dans des environnements multitâches du monde réel où les programmes se disputent les ressources. Avantage CISC - dont l'objectif de conception original était de compenser les CPU plus rapidement que les bus et la mémoire lente.

20

Vérifiez un peu à un emplacement arbitraire dans une variable de type arbitraire:

#define bit_test(x, y)  ( ( ((const char*)&(x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )

Exemple d'utilisation:

int main(void)
{
    unsigned char arr[8] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF };

    for (int ix = 0; ix < 64; ++ix)
        printf("bit %d is %d\n", ix, bit_test(arr, ix));

    return 0;
}

Remarques: Ceci est conçu pour être rapide (compte tenu de sa flexibilité) et non ramifié. Il en résulte un code machine SPARC efficace lors de la compilation de Sun Studio 8; Je l'ai également testé en utilisant MSVC ++ 2008 sur amd64. Il est possible de créer des macros similaires pour définir et effacer des bits. La principale différence de cette solution par rapport à beaucoup d'autres ici est qu'elle fonctionne pour n'importe quel emplacement dans à peu près n'importe quel type de variable.


20

Plus général, pour les bitmaps de taille arbitraire:

#define BITS 8
#define BIT_SET(  p, n) (p[(n)/BITS] |=  (0x80>>((n)%BITS)))
#define BIT_CLEAR(p, n) (p[(n)/BITS] &= ~(0x80>>((n)%BITS)))
#define BIT_ISSET(p, n) (p[(n)/BITS] &   (0x80>>((n)%BITS)))

2
CHAR_BITest déjà défini par limits.h, vous n'avez pas besoin de mettre le vôtre BITS(et en fait vous aggravez votre code en le faisant)
MM

14

Ce programme consiste à changer tout bit de données de 0 à 1 ou de 1 à 0:

{
    unsigned int data = 0x000000F0;
    int bitpos = 4;
    int bitvalue = 1;
    unsigned int bit = data;
    bit = (bit>>bitpos)&0x00000001;
    int invbitvalue = 0x00000001&(~bitvalue);
    printf("%x\n",bit);

    if (bitvalue == 0)
    {
        if (bit == 0)
            printf("%x\n", data);
        else
        {
             data = (data^(invbitvalue<<bitpos));
             printf("%x\n", data);
        }
    }
    else
    {
        if (bit == 1)
            printf("elseif %x\n", data);
        else
        {
            data = (data|(bitvalue<<bitpos));
            printf("else %x\n", data);
        }
    }
}

14

Si vous faites beaucoup de twiddling, vous voudrez peut-être utiliser des masques qui rendront le tout plus rapide. Les fonctions suivantes sont très rapides et toujours flexibles (elles permettent le twiddling de bits dans des bit maps de n'importe quelle taille).

const unsigned char TQuickByteMask[8] =
{
   0x01, 0x02, 0x04, 0x08,
   0x10, 0x20, 0x40, 0x80,
};


/** Set bit in any sized bit mask.
 *
 * @return    none
 *
 * @param     bit    - Bit number.
 * @param     bitmap - Pointer to bitmap.
 */
void TSetBit( short bit, unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;        // Index to byte.
    n = bit % 8;        // Specific bit in byte.

    bitmap[x] |= TQuickByteMask[n];        // Set bit.
}


/** Reset bit in any sized mask.
 *
 * @return  None
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
void TResetBit( short bit, unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;        // Index to byte.
    n = bit % 8;        // Specific bit in byte.

    bitmap[x] &= (~TQuickByteMask[n]);    // Reset bit.
}


/** Toggle bit in any sized bit mask.
 *
 * @return   none
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
void TToggleBit( short bit, unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;        // Index to byte.
    n = bit % 8;        // Specific bit in byte.

    bitmap[x] ^= TQuickByteMask[n];        // Toggle bit.
}


/** Checks specified bit.
 *
 * @return  1 if bit set else 0.
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
short TIsBitSet( short bit, const unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;    // Index to byte.
    n = bit % 8;    // Specific bit in byte.

    // Test bit (logigal AND).
    if (bitmap[x] & TQuickByteMask[n])
        return 1;

    return 0;
}


/** Checks specified bit.
 *
 * @return  1 if bit reset else 0.
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
short TIsBitReset( short bit, const unsigned char *bitmap)
{
    return TIsBitSet(bit, bitmap) ^ 1;
}


/** Count number of bits set in a bitmap.
 *
 * @return   Number of bits set.
 *
 * @param    bitmap - Pointer to bitmap.
 * @param    size   - Bitmap size (in bits).
 *
 * @note    Not very efficient in terms of execution speed. If you are doing
 *        some computationally intense stuff you may need a more complex
 *        implementation which would be faster (especially for big bitmaps).
 *        See (http://graphics.stanford.edu/~seander/bithacks.html).
 */
int TCountBits( const unsigned char *bitmap, int size)
{
    int i, count = 0;

    for (i=0; i<size; i++)
        if (TIsBitSet(i, bitmap))
            count++;

    return count;
}

Remarque, pour définir le bit 'n' dans un entier de 16 bits, vous procédez comme suit:

TSetBit( n, &my_int);

C'est à vous de vous assurer que le nombre de bits est dans la plage de la carte binaire que vous passez. Notez que pour les petits processeurs endian qui octets, mots, dwords, qwords, etc., se mappent correctement les uns aux autres en mémoire (raison principale pour laquelle les petits processeurs endian sont `` meilleurs '' que les processeurs big-endian, ah, je sens une guerre des flammes venir sur...).


2
N'utilisez pas de table pour une fonction qui peut être implémentée avec un seul opérateur. TQuickByteMask [n] est équivalent à (1 << n). De plus, rendre vos arguments courts est une très mauvaise idée. Le / et% sera en fait une division, pas un décalage de bits / au niveau du bit et, parce que la division signée par une puissance de 2 ne peut pas être implémentée au niveau du bit. Vous devez rendre le type d'argument unsigned int!
R .. GitHub STOP HELPING ICE

À quoi ça sert? Cela rend le code plus lent et plus difficile à lire? Je ne vois pas un seul avantage avec ça. 1u << n est plus facile à lire pour les programmeurs C et peut, espérons-le, être traduit en une seule instruction d'horloge du processeur. Votre division, d'autre part, sera traduite en quelque chose autour de 10 ticks, ou même aussi mauvais que jusqu'à 100 ticks, selon la façon dont l'architecture spécifique gère mal la division. Quant à la fonctionnalité bitmap, il serait plus logique d'avoir une table de recherche traduisant chaque index de bits en un index d'octets, pour optimiser la vitesse.
Lundin

2
Comme pour le big / little endian, le big endian mappera les entiers et les données brutes (par exemple des chaînes) de la même manière: de gauche à droite msb à lsb tout au long du bitmap. Bien que le petit endian mappe les entiers de gauche à droite en 7-0, 15-8, 23-18, 31-24, mais les données brutes sont toujours de gauche à droite msb en lsb. Alors, comment peu d'endian est meilleur pour votre algorithme particulier me dépasse complètement, il semble que ce soit le contraire.
Lundin

2
@R .. Un tableau peut être utile si votre plate-forme ne peut pas se déplacer efficacement, comme les anciens microprocesseurs à micropuce, mais bien sûr, la division dans l'échantillon est absolument inefficace
jeb

12

Utilisez ceci:

int ToggleNthBit ( unsigned char n, int num )
{
    if(num & (1 << n))
        num &= ~(1 << n);
    else
        num |= (1 << n);

    return num;
}

5
Eh bien, il utilise une ramification inefficace.
asdf

3
@asdf Le travail du compilateur est de produire le binaire le plus efficace, le travail du programmeur est d'écrire du code clair
MM

3
Ceci est une bonne démonstration de test, de réglage et d'effacement d'un bit particulier. Cependant, c'est une très mauvaise approche pour basculer un peu.
Ben Voigt

10

Élargir la bitsetréponse:

#include <iostream>
#include <bitset>
#include <string>

using namespace std;
int main() {
  bitset<8> byte(std::string("10010011");

  // Set Bit
  byte.set(3); // 10010111

  // Clear Bit
  byte.reset(2); // 10010101

  // Toggle Bit
  byte.flip(7); // 00010101

  cout << byte << endl;

  return 0;
}

10

Si vous souhaitez effectuer toutes ces opérations avec la programmation C dans le noyau Linux, je suggère d'utiliser les API standard du noyau Linux.

Voir https://www.kernel.org/doc/htmldocs/kernel-api/ch02s03.html

set_bit  Atomically set a bit in memory
clear_bit  Clears a bit in memory
change_bit  Toggle a bit in memory
test_and_set_bit  Set a bit and return its old value
test_and_clear_bit  Clear a bit and return its old value
test_and_change_bit  Change a bit and return its old value
test_bit  Determine whether a bit is set

Remarque: Ici, toute l'opération se déroule en une seule étape. Ainsi, tous ces éléments sont garantis atomiques même sur les ordinateurs SMP et sont utiles pour maintenir la cohérence entre les processeurs.


9

Visual C 2010, et peut-être de nombreux autres compilateurs, prennent directement en charge les opérations booléennes intégrées. Un bit a deux valeurs possibles, tout comme un booléen, afin que nous puissions utiliser des booléens à la place - même s'ils occupent plus d'espace qu'un seul bit dans mémoire dans cette représentation. Cela fonctionne, même l' sizeof()opérateur fonctionne correctement.

bool    IsGph[256], IsNotGph[256];

//  Initialize boolean array to detect printable characters
for(i=0; i<sizeof(IsGph); i++)  {
    IsGph[i] = isgraph((unsigned char)i);
}

Donc, à votre question, IsGph[i] =1ou IsGph[i] =0simplifiez le réglage et la suppression des bools.

Pour rechercher des caractères non imprimables:

//  Initialize boolean array to detect UN-printable characters, 
//  then call function to toggle required bits true, while initializing a 2nd
//  boolean array as the complement of the 1st.
for(i=0; i<sizeof(IsGph); i++)  {
    if(IsGph[i])    {
         IsNotGph[i] = 0;
    }   else   {
         IsNotGph[i] = 1;
    }
}

Notez qu'il n'y a rien de "spécial" dans ce code. Il traite un peu comme un entier - ce qui est techniquement le cas. Un entier de 1 bit pouvant contenir 2 valeurs et 2 valeurs uniquement.

J'ai déjà utilisé cette approche pour trouver des enregistrements de prêt en double, où loan_number était la clé ISAM, en utilisant le numéro de prêt à 6 chiffres comme index dans le tableau de bits. Sauvagement rapide et après 8 mois, a prouvé que le système mainframe dont nous obtenions les données fonctionnait en fait mal. La simplicité des tableaux de bits rend la confiance dans leur exactitude très élevée - par rapport à une approche de recherche par exemple.


std :: bitset est en effet implémenté sous forme de bits par la plupart des compilateurs
galinette

@galinette, d'accord. Le fichier d'en-tête #include <bitset> est une bonne ressource à cet égard. En outre, le vecteur de classe spéciale <bool> indique quand vous devez modifier la taille du vecteur. Le C ++ STL, 2nd Edition, Nicolai M. Josuttis les couvre de manière exhaustive sur les pages 650 et 281 respectivement. C ++ 11 ajoute quelques nouvelles capacités à std :: bitset, un intérêt particulier pour moi est une fonction de hachage dans des conteneurs non ordonnés. Merci pour l'information! Je vais supprimer mon commentaire sur les crampes cérébrales. Déjà assez de déchets sur le web. Je ne veux pas y ajouter.

3
Cela utilise au moins un octet entier de stockage pour chacun bool. Peut-être même 4 octets pour les configurations C89 qui utilisent intpour implémenterbool
MM

@MattMcNabb, vous avez raison. En C ++, la taille du type int nécessaire pour implémenter un booléen n'est pas spécifiée par la norme. J'ai réalisé que cette réponse était erronée il y a quelque temps, mais j'ai décidé de la laisser ici car les gens la trouvent apparemment utile. Pour ceux qui souhaitent utiliser les bits, le commentaire de galinette est le plus utile, tout comme ma bibliothèque de bits ici ... stackoverflow.com/a/16534995/1899861

2
@RocketRoy: Il vaut probablement la peine de changer la phrase qui prétend que c'est un exemple "d'opérations sur les bits".
Ben Voigt

6

Utilisez l'un des opérateurs définis ici .

Pour définir un bit, utilisé int x = x | 0x?;?est la position du bit sous forme binaire.


2
0xest le préfixe d'un littéral en hexadécimal, pas binaire.
Ben Voigt

5

Voici quelques macros que j'utilise:

SET_FLAG(Status, Flag)            ((Status) |= (Flag))
CLEAR_FLAG(Status, Flag)          ((Status) &= ~(Flag))
INVALID_FLAGS(ulFlags, ulAllowed) ((ulFlags) & ~(ulAllowed))
TEST_FLAGS(t,ulMask, ulBit)       (((t)&(ulMask)) == (ulBit))
IS_FLAG_SET(t,ulMask)             TEST_FLAGS(t,ulMask,ulMask)
IS_FLAG_CLEAR(t,ulMask)           TEST_FLAGS(t,ulMask,0)

5

Variable utilisée

int value, pos;

value - Data
pos - position du bit que nous souhaitons régler, effacer ou basculer.

Réglez un peu:

value = value | 1 << pos;

Clair un peu:

value = value & ~(1 << pos); 

Basculez un peu:

value = value ^ 1 << pos;

5
int set_nth_bit(int num, int n){    
    return (num | 1 << n);
}

int clear_nth_bit(int num, int n){    
    return (num & ~( 1 << n));
}

int toggle_nth_bit(int num, int n){    
    return num ^ (1 << n);
}

int check_nth_bit(int num, int n){    
    return num & (1 << n);
}

Le type de retour check_nth_bitpeut être bool.
Xeverous

@Xeverous oui cela dépend de l'intention des appelants
Sazzad Hissain Khan

5

Supposons d'abord quelques éléments
num = 55 Entier pour effectuer des opérations au niveau du bit (définir, obtenir, effacer, basculer).
n = 4Position de bit basée sur 0 pour effectuer des opérations au niveau du bit.

Comment obtenir un peu?

  1. Pour obtenir le nthbit de décalage à droite num num, nfois. Effectuez ensuite ET au niveau du bit &avec 1.
bit = (num >> n) & 1;

Comment ça fonctionne?

       0011 0111 (55 in decimal)
    >>         4 (right shift 4 times)
-----------------
       0000 0011
     & 0000 0001 (1 in decimal)
-----------------
    => 0000 0001 (final result)

Comment régler un peu?

  1. Pour définir un bit de nombre particulier. Décalage à gauche 1 nfois. Effectuez ensuite l'opération OR au niveau du bit |avec num.
num |= (1 << n);    // Equivalent to; num = (1 << n) | num;

Comment ça fonctionne?

       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
       0001 0000
     | 0011 0111 (55 in decimal)
-----------------
    => 0001 0000 (final result)

Comment effacer un peu?

  1. Décalage à gauche 1, nfois ie1 << n .
  2. Effectuez le complément au niveau du bit avec le résultat ci-dessus. Pour que le nième bit devienne non défini et que le reste du bit soit défini, c.-à-d.~ (1 << n) .
  3. Enfin, effectuez l'opération ET au niveau du bit &avec le résultat ci-dessus et num. Les trois étapes ci-dessus peuvent être écrites ensemble num & (~ (1 << n));

Étapes pour effacer un peu

num &= (~(1 << n));    // Equivalent to; num = num & (~(1 << n));

Comment ça fonctionne?

       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
     ~ 0001 0000
-----------------
       1110 1111
     & 0011 0111 (55 in decimal)
-----------------
    => 0010 0111 (final result)

Comment basculer un peu?

Pour basculer un peu, nous utilisons XOR au niveau du bit ^ opérateur . L'opérateur XOR au niveau du bit est évalué à 1 si le bit correspondant des deux opérandes est différent, sinon il est évalué à 0.

Cela signifie que pour basculer un peu, nous devons effectuer une opération XOR avec le bit que vous souhaitez basculer et 1.

num ^= (1 << n);    // Equivalent to; num = num ^ (1 << n);

Comment ça fonctionne?

  • Si le bit à basculer est 0 alors, 0 ^ 1 => 1 .
  • Si le bit à bascule est alors 1, 1 ^ 1 => 0.
       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
       0001 0000
     ^ 0011 0111 (55 in decimal)
-----------------
    => 0010 0111 (final result)

Lecture recommandée - Exercices d'opérateur au niveau du bit


Merci pour l'explication détaillée. Voici le lien pour le problème de la pratique pour BIT magique lien
Chandra Shekhar

4

Comment définir, effacer et basculer un seul bit?

Pour résoudre un problème de codage courant lors de la tentative de formation du masque:
1n'est pas toujours suffisamment large

Quels problèmes se produisent quand numberest un type plus large que 1?
xpeut être trop important pour le changement 1 << xconduisant à un comportement indéfini (UB). Même s'il xn'est pas trop grand, il ~peut ne pas retourner suffisamment de bits de poids fort.

// assume 32 bit int/unsigned
unsigned long long number = foo();

unsigned x = 40; 
number |= (1 << x);  // UB
number ^= (1 << x);  // UB
number &= ~(1 << x); // UB

x = 10;
number &= ~(1 << x); // Wrong mask, not wide enough

Pour assurer que 1 est suffisamment large:

Le code pourrait utiliser 1ullou pédantiquement (uintmax_t)1et laisser le compilateur optimiser.

number |= (1ull << x);
number |= ((uintmax_t)1 << x);

Ou cast - ce qui rend les problèmes de codage / révision / maintenance en gardant le cast correct et à jour.

number |= (type_of_number)1 << x;

Ou promouvez doucement le 1en forçant une opération mathématique au moins aussi large que le type de number.

number |= (number*0 + 1) << x;

Comme avec la plupart des manipulations de bits, à mieux travailler avec non signés types plutôt que signés les


Regard intéressant sur une vieille question! Ni approprié number |= (type_of_number)1 << x;ni number |= (number*0 + 1) << x;approprié pour définir le bit de signe d'un type signé ... En fait, ni l'un ni l'autre number |= (1ull << x);. Existe-t-il un moyen portable de le faire par position?
chqrlie

1
@chqrlie IMO, la meilleure façon d'éviter de définir le bit de signe et de risquer UB ou IDB avec des décalages est d'utiliser des types non signés . Le code signé par déplacement hautement portable est trop compliqué pour être acceptable.
chux

3

Une version basée sur un modèle C ++ 11 (placée dans un en-tête):

namespace bit {
    template <typename T1, typename T2> inline void set  (T1 &variable, T2 bit) {variable |=  ((T1)1 << bit);}
    template <typename T1, typename T2> inline void clear(T1 &variable, T2 bit) {variable &= ~((T1)1 << bit);}
    template <typename T1, typename T2> inline void flip (T1 &variable, T2 bit) {variable ^=  ((T1)1 << bit);}
    template <typename T1, typename T2> inline bool test (T1 &variable, T2 bit) {return variable & ((T1)1 << bit);}
}

namespace bitmask {
    template <typename T1, typename T2> inline void set  (T1 &variable, T2 bits) {variable |= bits;}
    template <typename T1, typename T2> inline void clear(T1 &variable, T2 bits) {variable &= ~bits;}
    template <typename T1, typename T2> inline void flip (T1 &variable, T2 bits) {variable ^= bits;}
    template <typename T1, typename T2> inline bool test_all(T1 &variable, T2 bits) {return ((variable & bits) == bits);}
    template <typename T1, typename T2> inline bool test_any(T1 &variable, T2 bits) {return variable & bits;}
}

Ce code est cassé. (Aussi, pourquoi avez-vous ;après vos définitions de fonction?)
melpomene

@melpomene Le code n'est pas cassé, je l'ai testé. Voulez-vous dire qu'il ne se compilera pas ou que le résultat est incorrect? À propos de l'extra ';' Je ne me souviens pas, ceux-ci peuvent être supprimés en effet.
Joakim L. Christiansen,

(variable & bits == bits)?
melpomene

Merci de l'avoir remarqué, c'était censé être((variable & bits) == bits)
Joakim L. Christiansen

utilisation std::bitseten c ++ 11
pqnet

0

Ce programme est basé sur la solution ci-dessus de @ Jeremy. Si quelqu'un souhaite jouer rapidement.

public class BitwiseOperations {

    public static void main(String args[]) {

        setABit(0, 4); // set the 4th bit, 0000 -> 1000 [8]
        clearABit(16, 5); // clear the 5th bit, 10000 -> 00000 [0]
        toggleABit(8, 4); // toggle the 4th bit, 1000 -> 0000 [0]
        checkABit(8,4); // check the 4th bit 1000 -> true 
    }

    public static void setABit(int input, int n) {
        input = input | ( 1 << n-1);
        System.out.println(input);
    }


    public static void clearABit(int input, int n) {
        input = input & ~(1 << n-1);
        System.out.println(input);
    }

    public static void toggleABit(int input, int n) {
        input = input ^ (1 << n-1);
        System.out.println(input);
    }

    public static void checkABit(int input, int n) {
        boolean isSet = ((input >> n-1) & 1) == 1; 
        System.out.println(isSet);
    }
}


Output :
8
0
0
true

-2

Essayez l'une de ces fonctions en langage C pour changer n bit:

char bitfield;

// Start at 0th position

void chang_n_bit(int n, int value)
{
    bitfield = (bitfield | (1 << n)) & (~( (1 << n) ^ (value << n) ));
}

Ou

void chang_n_bit(int n, int value)
{
    bitfield = (bitfield | (1 << n)) & ((value << n) | ((~0) ^ (1 << n)));
}

Ou

void chang_n_bit(int n, int value)
{
    if(value)
        bitfield |= 1 << n;
    else
        bitfield &= ~0 ^ (1 << n);
}

char get_n_bit(int n)
{
    return (bitfield & (1 << n)) ? 1 : 0;
}

value << npeut provoquer un comportement indéfini
MM
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.