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


Comment définir, effacer et basculer un peu en C / C ++?


2048
2017-09-07 00:42


origine


Réponses:


Définir un bit

Utilisez l'opérateur OU bit à bit (|) pour définir un peu.

number |= 1UL << n;

Cela va mettre le npeu de number.

Utilisation 1ULL si number est plus large que unsigned long; promotion de 1UL << n ne se produit qu'après avoir évalué 1UL << n où il est un comportement indéfini à déplacer de plus de la largeur d'un 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 va effacer le npeu de number. Vous devez inverser la chaîne de bits avec l'opérateur NOT (~), puis ET il.

Toggle un peu

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

number ^= 1UL << n;

Cela va basculer le npeu de number.

Vérification d'un bit

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

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

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

Cela mettra la valeur de la npeu de number dans la variable bit.

Changer le nth peu à X

Réglage de la npeu à l'un ou l'autre 1 ou 0 peut être réalisé avec ce qui suit sur une implémentation C ++ en complément de 2:

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

Bit n sera réglé si x est 1et effacé si x est 0. Si x a une autre valeur, vous obtenez des ordures. x = !!x le booléanise à 0 ou 1.

Pour rendre cela indépendant du comportement de négation du complément à 2 (où -1 si tous les bits sont définis, contrairement à l'implémentation d'un complément 1 ou d'une implémentation en C ++ / signe / magnitude), utilisez une 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 portable.

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


2997
2017-09-07 00:50



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

Ou la Renforcer version: boost::dynamic_bitset.

Il n'y a pas besoin de rouler les vôtres:

#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 bitet de taille d'exécution par rapport à un bibliothèque standard bitet de taille compilée.


381
2017-09-18 00:34



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 définir ou effacer un bit:

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 d'un peu:

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

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


212
2017-09-11 00:56



J'utilise des macros définies dans un fichier d'en-tête pour gérer les bits 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)))

/* 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))

125
2017-09-08 21:07



Il vaut parfois la peine d'utiliser un enum à prénom les bits:

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

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

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

pour définir, effacer et tester. De cette façon, vous cachez les nombres magiques du reste de votre code.

En dehors de cela, j'approuve la solution de Jeremy.


99
2017-09-17 02:04



De snip-c.zip's bitops.h:

/*
**  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 que vous semblez avoir des problèmes dans tous ceux-ci est "(1L << (posn))". Tout cela est de créer un masque avec un seul bit sur et qui fonctionnera avec n'importe quel type entier. L'argument "posn" spécifie le position où vous voulez le bit. Si posn == 0, alors cette expression évaluer à:

    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 à l'endroit spécifié position. La seule partie délicate est dans la macro BitClr () où nous devons définir un seul 0 bit dans un champ de 1. Ceci est accompli en utilisant les 1 complément de la même expression que l'opérateur tilde (~).

Une fois le masque créé, il est appliqué à l'argument comme vous le suggérez, en utilisant les opérateurs bit à bit et (&), ou (|) et xor (^). Depuis le masque est de type long, les macros fonctionneront aussi bien sur char, short, int, ou long.

L'essentiel est que c'est 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 l'une de ces macros avec des valeurs de masque explicites chaque fois que vous besoin d'un, mais pourquoi le faire? Rappelez-vous, la substitution de macro se produit dans le préprocesseur et donc le code généré reflétera le fait que les valeurs sont considérés comme constants par le compilateur - c'est-à-dire qu'il est tout aussi efficace à utiliser les macros généralisées pour "réinventer la roue" chaque fois que vous devez faire manipulation de bits.

Pas convaincu? Voici un code de test - J'ai utilisé Watcom C en pleine optimisation et sans utiliser _cdecl donc le démontage résultant serait 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] ------------------------------------------- ----------------------


34
2018-06-05 14:18



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

Exemple:

value is 0x55;
bitnum : 3rd.

le & 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: régler le bit

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

28
2017-09-07 00:45



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

Pour définir le dernier bit dans 000b:

foo = foo | 001b

Pour vérifier le dernier bit dans foo:

if ( foo & 001b ) ....

Pour effacer le dernier bit dans foo:

foo = foo & 110b

j'ai utilisé XXXb pour plus de clarté. Vous travaillerez probablement avec la représentation HEX, en fonction de la structure de données dans laquelle vous empaquetez des bits.


26
2017-07-13 06:53



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

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

Pour définir 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.


24
2018-06-14 15:23



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, au cours d'un débogage basé sur l'oscilloscope, j'ai été étonné de constater que ces méthodes ont un surcoût considérable dans les cycles CPU par rapport à écrire une valeur directement dans les registres PORTnSET / PORTnCLEAR du micro qui fait une différence réelle. les broches de basculement de fréquence ISR.

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


22
2017-11-06 11:30