Comment définissez-vous, effacer et basculer un seul bit?
Comment définir, effacer et basculer un peu en C / C++?
26 réponses
Définir un bit
Utiliser l'opérateur or (|
) pour définir un bit.
number |= 1UL << n;
Cela va définir le n
th bit de number
.
Utilisez 1ULL
Si number
est plus large que unsigned long
; la promotion de 1UL << n
ne se produit qu'après avoir évalué 1UL << n
où son comportement indéfini se déplace 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 (&
) pour effacer un bit.
number &= ~(1UL << n);
Cela effacera le n
th bit de number
. Vous devez inverser la chaîne de bits avec l'opérateur bitwise NOT (~
), then ET it.
Basculer un peu
L'opérateur XOR (^
) peut être utilisé pour basculer un bit.
number ^= 1UL << n;
Qui va basculer le n
th bit de number
.
Vérifier un peu
Vous n'avez pas demandé ça, mais je pourrais aussi bien l'ajouter.
Pour vérifier un bit, déplacez le nombre n vers la droite, puis il:
bit = (number >> n) & 1U;
Cela mettra la valeur du n
th bit de number
dans la variable bit
.
Modification de la n - ième bit de x
Définir le n
th bit sur 1
ou 0
peut être réalisé avec ce qui suit sur l'implémentation C++ d'un complément 2:
number ^= (-x ^ number) & (1UL << n);
Bits n
sera réglé que si x
est 1
, et effacé si x
est 0
. Si x
a une autre valeur, vous obtenez des ordures. {[35] } va le booleaniser à 0 ou 1.
Pour rendre cela indépendant du comportement de négation du complément 2 (où -1
a tous les bits définis, contrairement à l'implémentation C++ du complément ou du signe/magnitude 1), 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.
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 le wiki de la communauté répond plus loin bas) ou une sorte d'encapsulation.
Utilisation de la bibliothèque C++ Standard: std::bitset<N>
.
Ou la versionBoost : boost::dynamic_bitset
.
Il N'y a pas besoin de rouler votre propre:
#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 bitset de taille runtime par rapport à unbibliothèque standard bitset de taille compile.
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 de bits deviennent maintenant un peu (haha) plus simples:
Pour définir 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érifier 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 bit-twiddling décrites dans les messages précédents.
J'utilise des macros définies dans un fichier d'en-tête pour gérer bit set et clear:
/* 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))
Il vaut parfois la peine d'utiliser un enum
pourNom 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 définir, effacer et tester. De cette façon, vous cachez les nombres magiques du reste de votre code.
À part ça, j'approuve la solution de Jeremy.
De snip-c.zip les bitops de .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 commune avec laquelle vous semblez avoir des problèmes dans tout cela est "(1L
0000 0000 0000 0000 0000 0000 0000 0001 binary.
Si posn= = 8, il évaluera à
0000 0000 0000 0000 0000 0001 0000 0000 binary.
Dans les autres mots, il crée simplement un champ de 0 avec un 1 au spécifié position. 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 les 1 complément 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 bit à bit et (&), ou ( | ) et xor ( ^ ). Puisque le masque est de type long, les macros fonctionneront tout comme bien sur char, short, int, ou de long.
L'essentiel est que c'est une solution générale à toute une classe de problème. Il est, bien sûr, possible et même approprié de réécrire le équivalent de l'une de ces macros avec des valeurs de masque explicites chaque fois que vous besoin d'un, mais pourquoi faire? Rappelez-vous, la substitution de macro se produit dans le préprocesseur et ainsi le code généré reflétera le fait que les valeurs sont considérés comme constants par le compilateur - dire qu'elle est tout aussi efficace pour utiliser les macros généralisées à "réinventer la roue" chaque fois que vous devez faire de manipulation de bits.
Pas convaincu? Voici un code de test - J'ai utilisé Watcom C avec une optimisation complète et sans utiliser _cdecl, 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 ]-----------------------------------------------------------------
Pour le débutant que je voudrais expliquer un peu plus avec un exemple:
Exemple:
value is 0x55;
bitnum : 3rd.
L'opérateur &
est utilisé vérifiez 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)
Bascule ou bascule:
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)
Utiliser les opérateurs binaires: &
|
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 une représentation hexadécimale, en fonction de la structure de données dans laquelle vous emballez des bits.
Voici ma macro arithmétique de bits préférée, qui fonctionne pour tout type de tableau entier non signé de unsigned char
à 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 définir un bit:
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.
Comme il est étiqueté "embedded", je suppose que vous utilisez un microcontrôleur. Toutes les suggestions ci-dessus sont valides 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 une surcharge considérable en 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 / basculement ISR haute fréquence piges.
Pour ceux qui ne sont pas familiers: dans mon exemple, le micro a un Portn de Registre général d'état de broche qui reflète les broches de sortie, Donc PORTn / = BIT_TO_SET entraîne une lecture-modification-écriture dans ce registre. Cependant, les registres PORTnSET / PORTnCLEAR prennent un ' 1 'pour signifier" please make this bit 1 "(SET) ou" please make this bit zero "(CLEAR) et un '0' pour signifier"laisser le code PIN seul". donc, 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.
L'approche bitfield présente d'autres avantages dans l'arène embarqué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 d'emballage des bits - je pense que C'est MSB d'abord, mais cela peut dépendre de l'implémentation. Vérifiez également comment les champs de vos gestionnaires de compilateur traversent les limites des octets.
Vous pouvez alors lire, écrire, tester les valeurs individuelles comme avant.
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)))
Vérifier 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;
}
Notes: 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 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 les bits. La principale différence de cette solution par rapport à beaucoup d'autres ici est que cela fonctionne pour n'importe quel endroit dans pratiquement n'importe quel type de variable.
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 sont toujours flexibles (elles permettent de tourner des bits dans des cartes de bits 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, 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 de bits que vous passez. Notez que pour little endian processeurs d'octets, de mots, dword, qwords, etc., carte correctement les uns aux autres en mémoire (raison principale que les processeurs little endian sont "meilleurs" que les processeurs big-endian, ah, je sens une guerre de flamme à venir...).
Ce programme est de changer toutes les données bits 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);
}
}
}
Utilisez ceci:
int ToggleNthBit ( unsigned char n, int num )
{
if(num & (1 << n))
num &= ~(1 << n);
else
num |= (1 << n);
return num;
}
Expansion de la réponse bitset
:
#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;
}
Si vous voulez effectuer toute cette opération avec la programmation C dans le noyau Linux alors je suggère d'utiliser des 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
Note: ici, toute l'opération se passe en une seule étape. Donc, tous ceux-ci sont garantis atomic même sur les ordinateurs SMP et sont utiles pour maintenir la cohérence entre les processeurs.
Visual C 2010, et peut-être beaucoup d'autres compilateurs, ont un support direct pour les opérations de bits intégrées. Étonnamment, cela fonctionne, même l'opérateur sizeof() 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] = 1, ou IsGph[i] = 0 rendent le réglage et l'effacement des bools faciles.
Pour trouver 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" à propos de ce code. Il traite un peu comme un entier - ce qui techniquement, c'est le cas. Un entier de 1 bit pouvant contenir 2 valeurs et 2 valeurs seulement.
J'ai une fois 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, prouvé que le système mainframe nous obtenions les données de était en fait dysfonctionnement. La simplicité des tableaux de bits rend la confiance dans leur exactitude très élevée-par rapport à une approche de recherche par exemple.
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)
Comment définir, effacer et basculer un seul bit?
Pour traiter une codification commune piège lors d'une tentative pour former le masque:1
n'est pas toujours assez large
Quels problèmes se produisent lorsque number
est un type plus large que 1
?x
peut-être trop grand pour la maj 1 << x
conduisant à comportement indéfini (UB). Même si x
n'est pas trop grand, ~
peut ne pas retourner assez de bits les plus significatifs.
// 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 1 est assez large:
Le Code pourrait utiliser 1ull
ou pedantiquement (uintmax_t)1
et 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 la distribution correcte et à jour.
number |= (type_of_number)1 << x;
Ou promouvoir doucement le 1
en forçant une opération mathématique qui est aussi large que le type de number
.
number |= (number*0 + 1) << x;
Comme avec la plupart des bits de manipulations, il est préférable de travailler avec unsigned les types plutôt que signée les uns
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);
}
Une version de modèle C++11 (mise 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;}
}
Variable utilisée
int value, pos;
Valeur-Données
pos-position du bit que nous sommes intéressés à définir, effacer ou basculer
définir un bit
value = value | 1 << pos;
Clair un peu
value = value & ~(1 << pos);
Bascule un peu
value = value ^ 1 << pos;
Essayez l'une de ces fonctions dans le langage C pour changer n bits:
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;
}