examples bitwise c++ c bit-manipulation bitwise-operators
snip-c.zip

c++ - bitwise - ¿Cómo configura, borra y alterna un solo bit?



bitwise operators python (26)

¿Cómo configura, borra y alterna un poco en C / C ++?


¿Cómo configura, borra y alterna un solo bit?

Para abordar un error de codificación común al intentar formar la máscara:
1no siempre es lo suficientemente ancho

¿Qué problemas pasan cuando numberes un tipo más amplio que 1?
xpuede ser demasiado grande para el cambio que 1 << xconduce a un comportamiento indefinido (UB). Incluso si xno es demasiado grande, ~puede que no se volteen suficientes bits más significativos.

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

Para asegurar 1 es lo suficientemente ancho:

El código podría usar 1ullo pedanticalmente (uintmax_t)1y permitir que el compilador optimice.

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

O cast: lo que hace que los problemas de codificación / revisión / mantenimiento se mantengan correctos y actualizados.

number |= (type_of_number)1 << x;

O bien, promueva suavemente 1forzando una operación matemática que sea al menos tan amplia como el tipo de number.

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

Al igual que con la mayoría de las manipulaciones de bits, mejor trabajar con sin signo tipos en lugar de firmados los


Poniendo un poco

Use el operador OR a nivel de bits ( | ) para establecer un bit.

number |= 1UL << n;

Eso establecerá el n º bit de number . n debe ser cero, si desea establecer el primer bit y así sucesivamente hasta n-1 , si desea establecer el n º bit.

Use 1ULL si el number es más ancho que el unsigned long ; la promoción de 1UL << n no ocurre hasta después de evaluar 1UL << n donde el comportamiento indefinido cambia más que el ancho de un long . Lo mismo se aplica a todos los demás ejemplos.

Despejando un poco

Use el operador AND a nivel de bits ( & ) para borrar un poco.

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

Eso borrará el número n de number . Debe invertir la cadena de bits con el operador NO a nivel de bits ( ~ ), y luego Y.

Toggling un poco

El operador XOR ( ^ ) se puede utilizar para alternar un bit.

number ^= 1UL << n;

Eso cambiará el número n de number .

Revisando un poco

No pediste esto, pero también podría agregarlo.

Para verificar un bit, desplace el número n a la derecha, luego a bit y Y:

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

Eso pondrá el valor del n º bit de number en el bit variable.

Cambiando el bit n a x

La configuración del bit n en 1 o 0 se puede lograr con lo siguiente en una implementación de C ++ de complemento a 2:

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

El bit n se establecerá si x es 1 , y se borrará si x es 0 . Si x tiene algún otro valor, obtienes basura. x = !!x booleanize a 0 o 1.

Para hacer que este comportamiento de negación del complemento de 2 sea independiente (donde -1 tiene todos los bits establecidos, a diferencia del complemento de 1 o la implementación de C ++ de signo / magnitud), use la negación sin signo.

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

o

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

En general, es una buena idea usar tipos sin firma para la manipulación de bits portátil.

En general, también es una buena idea no copiar / pegar el código en general, por lo que muchas personas usan macros de preprocesador (como la respuesta de la wiki de la comunidad más abajo ) o algún tipo de encapsulación.


Compruebe un bit en una ubicación arbitraria en una variable de tipo arbitrario:

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

Uso de la muestra:

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; }

Notas: Esto está diseñado para ser rápido (dada su flexibilidad) y no ramificado. Da como resultado un eficiente código de máquina SPARC cuando se compila Sun Studio 8; También lo he probado usando MSVC ++ 2008 en amd64. Es posible hacer macros similares para configurar y borrar bits. La diferencia clave de esta solución en comparación con muchas otras aquí es que funciona para cualquier ubicación en prácticamente cualquier tipo de variable.


De los snip-c.zip 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, analicemos las cosas ...

La expresión común con la que pareces tener problemas en todo esto es "(1L << (posn))". Todo esto es crear una máscara con un solo bit y que funcionará con cualquier tipo de entero. El argumento "posn" especifica la posición en la que desea el bit. Si posn == 0, entonces esta expresión se evaluará como:

0000 0000 0000 0000 0000 0000 0000 0001 binary.

Si posn == 8, evaluará a

0000 0000 0000 0000 0000 0001 0000 0000 binary.

En otras palabras, simplemente crea un campo de 0 con un 1 en la posición especificada. La única parte difícil está en la macro BitClr () donde necesitamos configurar un solo bit 0 en un campo de 1. Esto se logra utilizando el complemento de 1 de la misma expresión que indica el operador de tilde (~).

Una vez que se crea la máscara, se aplica al argumento tal como sugiere, mediante el uso de los operadores a nivel de bits y (&), o (|) y xor (^). Como la máscara es del tipo long, las macros funcionarán igual de bien en las de char, short, int o long.

La conclusión es que esta es una solución general para toda una clase de problemas. Por supuesto, es posible e incluso apropiado reescribir el equivalente de cualquiera de estas macros con valores de máscara explícitos cada vez que lo necesite, pero ¿por qué hacerlo? Recuerde, la sustitución de macros se produce en el preprocesador y, por lo tanto, el código generado reflejará el hecho de que el compilador considera que los valores son constantes, es decir, es tan eficiente usar las macros generalizadas como para "reinventar la rueda" cada vez que necesite hacer la manipulación de bits.

¿No está convencido? Aquí hay un código de prueba: usé Watcom C con optimización completa y sin usar _cdecl, por lo que el desmontaje resultante sería lo más limpio posible:

---- [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 (desmontado)] -------------------------------------- ---------

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] ------------------------------------------- ----------------------


A veces vale la pena usar una enum para nombrar los bits:

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

Luego usa los nombres más adelante. Es decir, escribir

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

para configurar, borrar y probar. De esta manera ocultas los números mágicos del resto de tu código.

Aparte de eso apoyo la solución de Jeremy.


Ampliando la respuesta del conjunto de 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; }


Aquí está mi macro aritmética de bits favorita, que funciona para cualquier tipo de matriz de enteros sin signo desde caracteres unsigned char hasta size_t (que es el tipo más grande con el que debería ser eficiente trabajar):

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

Para establecer un bit:

BITOP(array, bit, |=);

Para borrar un poco:

BITOP(array, bit, &=~);

Para alternar un poco:

BITOP(array, bit, ^=);

Para probar un poco:

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

etc.


Como esto está etiquetado como "incrustado", asumiré que estás usando un microcontrolador. Todas las sugerencias anteriores son válidas y de trabajo (lectura-modificación-escritura, uniones, estructuras, etc.).

Sin embargo, durante un combate de depuración basada en el osciloscopio, me sorprendió descubrir que estos métodos tienen una sobrecarga considerable en los ciclos de la CPU en comparación con la escritura de un valor directamente en los registros PORTnSET / PORTnCLEAR de los micrófonos, lo que marca una diferencia real cuando hay bucles ajustados / altos -la frecuencia de los pines de conmutación de ISR.

Para aquellos que no están familiarizados: en mi ejemplo, el micro tiene un registro general de estado de PIN que refleja los pines de salida, por lo que hacer PORTn | = BIT_TO_SET da como resultado una lectura-modificación-escritura en ese registro. Sin embargo, los registros PORTnSET / PORTnCLEAR toman un ''1'' para significar "haga este bit 1" (SET) o "haga este bit cero" (CLEAR) y un ''0'' significa "dejar el pin solo". por lo tanto, termina con dos direcciones de puerto dependiendo de si está configurando o borrando el bit (no siempre es conveniente) pero una reacción mucho más rápida y un código ensamblado más pequeño.


El enfoque de campo de bits tiene otras ventajas en el campo incrustado. Puede definir una estructura que se asigna directamente a los bits en un registro de hardware particular.

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;

Debe tener en cuenta el orden de empaquetado de bits: creo que primero es MSB, pero esto puede depender de la implementación. Además, verifique cómo los campos de los controladores de su compilador cruzan los límites de bytes.

Luego puede leer, escribir, probar los valores individuales como antes.


Este programa es para cambiar cualquier bit de datos de 0 a 1 o 1 a 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); } } }


La otra opción es usar campos de bits:

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

define un campo de 3 bits (en realidad, son tres campos de 1 bit). Las operaciones de bits ahora se vuelven un poco más simples (jaja):

Para establecer o borrar un poco:

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

Para alternar un poco:

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

Comprobando un poco:

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

Esto solo funciona con campos de bits de tamaño fijo. De lo contrario, tendrá que recurrir a las técnicas de twiddling de bits descritas en publicaciones anteriores.


Más general, para mapas de bits de tamaño arbitrario:

#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)))


Para el principiante me gustaría explicar un poco más con un ejemplo:

Ejemplo:

value is 0x55; bitnum : 3rd.

El operador & se utiliza comprobar el 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)

Alternar o voltear:

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

| operador: establecer el bit

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


Si estás haciendo un poco de twiddling es posible que desees usar máscaras que harán que todo sea más rápido. Las siguientes funciones son muy rápidas y todavía son flexibles (permiten la combinación de bits en mapas de bits de cualquier tamaño).

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; }

Tenga en cuenta que para establecer el bit ''n'' en un entero de 16 bits, haga lo siguiente:

TSetBit( n, &my_int);

Depende de usted asegurarse de que el número de bits esté dentro del rango del mapa de bits que pasa. Tenga en cuenta que para los procesadores little endian, los bytes, palabras, palabras clave, qwords, etc., se asignan correctamente entre sí en la memoria (razón principal por la que los procesadores endian son "mejores" que los procesadores big-endian, ah, siento que se avecina una guerra de llamas en...).


Usando la biblioteca estándar de C ++: std::bitset<N> .

O la versión Boost : boost::dynamic_bitset .

No hay necesidad de rodar su propio:

#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 versión Boost permite un conjunto de bits de tamaño de tiempo de ejecución en comparación con un conjunto de bits de tamaño de compilación de biblioteca estándar .


Uso macros definidas en un archivo de encabezado para manejar el conjunto de bits y borrar:

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


Utilice los operadores bitwise: & |

Para establecer el último bit en 000b :

foo = foo | 001b

Para comprobar el último bit en foo :

if ( foo & 001b ) ....

Para borrar el último bit en foo :

foo = foo & 110b

Utilicé XXXb para mayor claridad. Probablemente trabajará con la representación HEX, dependiendo de la estructura de datos en la que esté empaquetando bits.


Utilizar esta:

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


Aquí hay algunas macros que utilizo:

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)


Pruebe una de estas funciones en el lenguaje C para cambiar n bits:

char bitfield; // Start at 0th position void chang_n_bit(int n, int value) { bitfield = (bitfield | (1 << n)) & (~( (1 << n) ^ (value << n) )); }

O

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

O

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; }


Una versión con plantilla de C ++ 11 (poner en un encabezado):

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 utilizada

int value, pos;

valor -
Pos. datos : posición del bit que nos interesa configurar, borrar o alternar
Establecer un bit

value = value | 1 << pos;

Claro un poco

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

Cambiar un poco

value = value ^ 1 << pos;


Visual C 2010, y quizás muchos otros compiladores, tienen soporte directo para operaciones de bits integradas. Sorprendentemente, esto funciona, incluso el operador sizeof () funciona correctamente.

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); }

Entonces, para su pregunta, IsGph [i] = 1, o IsGph [i] = 0 hace que la configuración y la eliminación de bools sean fáciles.

Para encontrar personajes no imprimibles ...

// 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; } }

Tenga en cuenta que no hay nada "especial" sobre este código. Se trata un poco como un entero, lo que técnicamente es. Un entero de 1 bit que puede contener 2 valores y 2 valores solamente.

Una vez utilicé este método para encontrar registros de préstamos duplicados, donde número de préstamo era la clave ISAM, utilizando el número de préstamo de 6 dígitos como un índice en la matriz de bits. Salvajemente rápido, y después de 8 meses, demostró que el sistema de mainframe del que obteníamos los datos funcionaba mal. La simplicidad de las matrices de bits hace que la confianza en su corrección sea muy alta; por ejemplo, un enfoque de búsqueda.


Si desea realizar esta operación completa con programación en C en el kernel de Linux , sugiero utilizar las API estándar del kernel de Linux.

Ver 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

Nota: Aquí toda la operación ocurre en un solo paso. Por lo tanto, se garantiza que todos estos elementos son atómicos incluso en computadoras SMP y son útiles para mantener la coherencia entre los procesadores.


Utilice uno de los operadores como se define here .

Para establecer un bit, se utiliza int x = x | 0x?;donde ?está la posición del bit en forma binaria.


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); }