¿Cómo establece, limpia y alterna un solo bit?


Respuestas:


3600

Establecer un poco

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

number |= 1UL << n;

Eso establecerá el nth bit de number. ndebe ser cero, si desea establecer el 1bit st y así sucesivamente n-1, si desea establecer el nbit th.

Use 1ULLif numberes más ancho que unsigned long; la promoción de 1UL << nno ocurre hasta después de evaluar 1UL << ndónde es un comportamiento indefinido cambiar más de la anchura de a long. Lo mismo se aplica a todos los demás ejemplos.

Despejando un poco

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

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

Eso despejará la nparte de number. Debe invertir la cadena de bits con el operador NOT bit a bit ( ~), luego Y.

Alternar un poco

El operador XOR ( ^) se puede usar para alternar un poco.

number ^= 1UL << n;

Eso alternará la parte de nth number.

Comprobando un poco

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

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

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

Eso pondrá el valor del nth bit de numberen la variable bit.

Cambiar el n ésimo bit a x

Establecer el nbit th en cualquiera 1o 0se puede lograr con lo siguiente en una implementación C ++ del complemento a 2:

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

El bit nse establecerá si xes 1y se borrará si xes así 0. Si xtiene algún otro valor, obtienes basura. x = !!xlo booleanizará a 0 o 1.

Para que esto sea independiente del comportamiento de negación del complemento de 2 (donde -1tiene todos los bits establecidos, a diferencia de un complemento de 1 o implementación de signo / magnitud C ++), 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 signo para la manipulación de bits portátil.

o

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

(number & ~(1UL << n))borrará el nbit th y (x << n)establecerá el nbit th en x.

También es generalmente una buena idea no copiar / pegar código en general, y 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.


128
Me gustaría señalar que en las plataformas que tienen soporte nativo para bit set / clear (por ejemplo, microcontroladores AVR), los compiladores a menudo traducirán 'myByte | = (1 << x)' en las instrucciones nativas de set / bit bit siempre que x sea una constante, por ejemplo: (1 << 5), o constante sin signo x = 5.
Aaron

52
bit = número & (1 << x); no pondrá el valor de bit x en bit a menos que el bit tenga el tipo _Bool (<stdbool.h>). De lo contrario, bit = !! (número & (1 << x)); will ..
Chris Young

23
por qué no cambias el último abit = (number >> x) & 1
aaronman

42
1es un intliteral, que está firmado. Entonces, todas las operaciones aquí operan con números firmados, que no están bien definidos por los estándares. Los estándares no garantizan el complemento de dos o el cambio aritmético, por lo que es mejor usarlo 1U.
Siyuan Ren

50
Prefiero number = number & ~(1 << n) | (x << n);cambiar el enésimo bit a x.
jiasli

459

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

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

No hay necesidad de rodar el suyo:

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


34
+1. No es que std :: bitset se pueda usar desde "C", pero como el autor etiquetó su pregunta con "C ++", AFAIK, su respuesta es la mejor por aquí ... std :: vector <bool> es otra forma, si uno sabe sus pros y sus contras
paercebal

23
@andrewdotnich: vector <bool> es (desafortunadamente) una especialización que almacena los valores como bits. Ver gotw.ca/publications/mill09.htm para más información ...
Niklas

71
Tal vez nadie lo mencionó porque esto fue etiquetado como incrustado. En la mayoría de los sistemas integrados, evitas STL como la peste. Y es probable que el soporte de impulso sea un ave muy rara para detectar entre la mayoría de los compiladores integrados.
Lundin

17
@ Martin Es muy cierto. Además de los asesinos específicos de rendimiento como STL y plantillas, muchos sistemas integrados incluso evitan completamente las bibliotecas estándar completas, porque es muy difícil de verificar. La mayor parte de la rama incrustada está adoptando estándares como MISRA, que requiere herramientas de análisis de código estático (cualquier profesional de software debería usar tales herramientas por cierto, no solo las personas incrustadas). En general, las personas tienen mejores cosas que hacer que ejecutar un análisis estático en toda la biblioteca estándar, si su código fuente está disponible para ellos en el compilador específico.
Lundin

37
@Lundin: Sus declaraciones son excesivamente amplias (por lo tanto, inútiles para discutir). Estoy seguro de que puedo encontrar situaciones en las que son ciertas. Esto no cambia mi punto inicial. Ambas clases están perfectamente bien para su uso en sistemas integrados (y sé con certeza que se usan). Su punto inicial acerca de que STL / Boost no se usa en sistemas integrados también es incorrecto. Estoy seguro de que hay sistemas que no los usan e incluso los sistemas que los usan se usan con prudencia, pero decir que no se usan simplemente no es correcto (porque hay sistemas en los que se usan).
Martin York

248

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 bit ahora se vuelven un poco (jaja) más simples:

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, debe recurrir a las técnicas de cambio de bits descritas en publicaciones anteriores.


68
Siempre he encontrado que usar bitfields es una mala idea. No tiene control sobre el orden en que se asignan los bits (desde la parte superior o inferior), lo que hace que sea imposible serializar el valor de forma estable / portátil, excepto bit a la vez. También es imposible mezclar aritmética de bits de bricolaje con campos de bits, por ejemplo, hacer una máscara que pruebe varios bits a la vez. Por supuesto, puede usar && y esperar que el compilador lo optimice correctamente ...
R .. GitHub DEJE DE AYUDAR A ICE

34
Los campos de bits son malos de muchas maneras, casi podría escribir un libro al respecto. De hecho, casi tuve que hacer eso para un programa de campo de bits que necesitaba el cumplimiento de MISRA-C. MISRA-C exige que se documente todo el comportamiento definido por la implementación, por lo que terminé escribiendo un ensayo sobre todo lo que puede salir mal en los campos de bits. Orden de bits, endianess, bits de relleno, bytes de relleno, varios otros problemas de alineación, conversiones de tipo implícito y explícito hacia y desde un campo de bits, UB si no se usa int y así sucesivamente. En su lugar, utilice operadores bit a bit para menos errores y código portátil. Los campos de bits son completamente redundantes.
Lundin

44
Como la mayoría de las características del lenguaje, los campos de bits se pueden usar correctamente o se puede abusar de ellos. Si necesita empaquetar varios valores pequeños en un solo int, los campos de bits pueden ser muy útiles. Por otro lado, si comienza a hacer suposiciones sobre cómo los campos de bits se asignan al int que contiene real, solo está buscando problemas.
Ferruccio

44
@endolith: Esa no sería una buena idea. Podría hacerlo funcionar, pero no necesariamente sería portátil a un procesador diferente, a un compilador diferente o incluso a la próxima versión del mismo compilador.
Ferruccio

3
@Yasky y Ferruccio, obteniendo diferentes respuestas a sizeof () para este enfoque, deberían ilustrar los problemas de compatibilidad no solo entre los compiladores sino también a través del hardware. A veces nos engañamos a nosotros mismos de que hemos resuelto estos problemas con idiomas o tiempos de ejecución definidos, pero realmente se reduce a '¿funcionará en mi máquina?'. Ustedes incrustados tienen mi respeto (y simpatía).
Kelly S. French

181

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

17
Eh, me doy cuenta de que esta es una publicación de 5 años, pero no hay duplicación de argumentos en ninguna de esas macros, Dan
Robert Kelly

11
BITMASK_CHECK(x,y) ((x) & (y))debe ser de lo ((x) & (y)) == (y)contrario, devuelve un resultado incorrecto en la máscara multibit (ej. 5vs. 3) / * Hola a todos los sepultureros
:)

77
1debería ser (uintmax_t)1o similar en caso de que alguien intente usar estas macros en un longtipo o más grande
MM

2
BITMASK_CHECK_ALL(x,y)se puede implementar como!~((~(y))|(x))
Handy999

3
@ Handy999 Es un poco más fácil ver por qué eso funciona después de aplicar la ley de De Morgan y reorganizar para obtener!(~(x) & (y))
Tavian Barnes

114

A veces vale la pena usar un enumpara nombrar los bits:

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

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

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

para establecer, borrar y probar. De esta manera, oculta los números mágicos del resto de su código.

Aparte de eso, respaldo la solución de Jeremy.


1
Alternativamente, podría hacer una clearbits()función en lugar de &= ~. ¿Por qué estás usando una enumeración para esto? Pensé que eran para crear un montón de variables únicas con un valor arbitrario oculto, pero estás asignando un valor definido a cada una. Entonces, ¿cuál es el beneficio en comparación con solo definirlos como variables?
endolito

44
@endolith: El uso de enums para conjuntos de constantes relacionadas se remonta mucho en la programación de c. Sospecho que con los compiladores modernos, la única ventaja sobre, const shorto lo que sea, es que están agrupados explícitamente. Y cuando los quiere para algo más que máscaras de bits, obtiene la numeración automática. En c ++, por supuesto, también forman tipos distintos que le brindan un poco más de comprobación de errores estáticos.
dmckee --- ex-gatito moderador

Entrarás en constantes de enumeración indefinidas si no defines una constante para cada uno de los valores posibles de los bits. ¿Para qué enum ThingFlagssirve ThingError|ThingFlag1, por ejemplo?
Luis Colorado

66
Si utiliza este método, tenga en cuenta que las constantes de enumeración siempre son de tipo con signo int. Esto puede causar todo tipo de errores sutiles debido a la promoción de números enteros implícitos u operaciones bit a bit en tipos firmados. thingstate = ThingFlag1 >> 1invocará, por ejemplo, un comportamiento definido por la implementación. thingstate = (ThingFlag1 >> x) << ypuede invocar un comportamiento indefinido. Y así. Para estar seguro, utilice siempre un tipo sin signo.
Lundin

1
@Lundin: a partir de C ++ 11, puede establecer el tipo subyacente de una enumeración, por ejemplo: enum My16Bits: unsigned short { ... };
Aiken Drum

47

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

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

0000 0000 0000 0000 0000 0000 0000 0001 binary.

Si posn == 8, se evaluará para:

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 es en la macro BitClr () donde necesitamos establecer un solo 0 bit en un campo de 1. Esto se logra utilizando el complemento de 1 de la misma expresión que denota el operador tilde (~).

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

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 ocurre en el preprocesador, por lo que 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 "reinventar la rueda" cada vez que necesite hacer manipulación de bits.

Poco convencido? Aquí hay un código de prueba: utilicé 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] ------------------------------------------- ----------------------


3
Dos cosas sobre esto: (1) al examinar detenidamente sus macros, algunos pueden creer incorrectamente que las macros realmente establecen / borran / voltean los bits en el argumento, sin embargo no hay asignación; (2) su prueba.c no está completa; Sospecho que si ejecutara más casos encontraría un problema (ejercicio de lectura)
Dan

19
-1 Esto es simplemente una ofuscación extraña. Nunca reinventes el lenguaje C ocultando la sintaxis del lenguaje detrás de las macros, es una muy mala práctica. Luego, algunas rarezas: primero, 1L está firmado, lo que significa que todas las operaciones de bits se realizarán en un tipo firmado. Todo lo que se pasa a estas macros volverá como se firmó durante mucho tiempo. No está bien. En segundo lugar, esto funcionará de manera muy ineficiente en CPU más pequeñas, ya que se aplica durante mucho tiempo cuando las operaciones podrían haber estado en el nivel int. Tercero, las macros funcionales son la raíz de todo mal: no tienes ningún tipo de seguridad. Además, el comentario anterior sobre ninguna asignación es muy válido.
Lundin

2
Esto fallará si arges así long long. 1Lnecesita ser el tipo más amplio posible, entonces (uintmax_t)1. (Puede salirse con la suya 1ull)
MM

¿Optimizaste para el tamaño del código? En las CPU convencionales de Intel, obtendrá paradas de registro parcial al leer AX o EAX después de que esta función regrese, porque escribe los componentes de 8 bits de EAX. (Está bien en las CPU AMD u otras que no cambien el nombre de los registros parciales por separado del registro completo. Haswell / Skylake no cambian el nombre de AL por separado, pero cambian el nombre de AH ).
Peter Cordes

37

Use los operadores bit a bit: & |

Para configurar 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

Solía XXXbpor claridad. Probablemente trabajará con representación HEX, dependiendo de la estructura de datos en la que está empacando bits.


77
No hay notación binaria en C. Las constantes enteras binarias son una extensión no estándar.
Lundin

Use XOR para alternar un poco:foo = foo ^ MY_MASK
Peter L

Utilice NO para invertir una máscara para hacer claro:foo = foo & ~MY_MASK
Peter L

32

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: establece el bit

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

26

Aquí está mi macro aritmética de bits favorita, que funciona para cualquier tipo de matriz entera sin signo de unsigned charhasta 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 poco:

BITOP(array, bit, |=);

Para despejar un poco:

BITOP(array, bit, &=~);

Para alternar un poco:

BITOP(array, bit, ^=);

Para probar un poco:

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

etc.


55
Es bueno leer, pero hay que tener en cuenta los posibles efectos secundarios. El uso BITOP(array, bit++, |=);en bucle probablemente no hará lo que la persona que llama desea.
miedo el

En efecto. =) Una variante que puede preferir es separarla en 2 macros, 1 para abordar el elemento de matriz y la otra para cambiar el bit a su lugar, ala BITCELL(a,b) |= BITMASK(a,b);(ambos toman acomo argumento para determinar el tamaño, pero este último nunca se evaluaría aya que aparece solo en sizeof).
R .. GitHub DEJA DE AYUDAR AL HIELO

1
@R .. Esta respuesta es muy antigua, pero probablemente prefiera una función a una macro en este caso.
PC Luddite

Menor: la tercera (size_t)elenco parece ser no sólo para asegurar cierta matemáticas sin firmar con %. Podría (unsigned)allí.
chux - Restablecer Monica

Lo (size_t)(b)/(8*sizeof *(a))innecesariamente podría reducirse bantes de la división. Solo un problema con matrices de bits muy grandes. Sigue siendo una macro interesante.
chux - Restablecer Monica

25

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

Sin embargo, durante una serie 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 escribir un valor directamente en los registros PORTnSET / PORTnCLEAR del micro, lo que hace una diferencia real cuando hay bucles estrechos / altos -frecuencia ISR pasadores de alternancia.

Para aquellos que no están familiarizados: en mi ejemplo, el micro tiene un registro general de estado de pin PORTn 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 "por favor haga este bit 1" (SET) o "por favor haga que este bit sea cero" (CLEAR) y un '0' para significar "deje el pin en paz". entonces, terminas con dos direcciones de puerto dependiendo de si estás 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.


Micro fue Coldfire MCF52259, usando C en Codewarrior. Mirar el desensamblador / asm es un ejercicio útil, ya que muestra todos los pasos que la CPU debe seguir para realizar incluso la operación más básica. <br> También vimos otras instrucciones de acaparamiento de CPU en bucles de tiempo crítico: restringir una variable haciendo var% = max_val cuesta una pila de ciclos de CPU cada vez, mientras que si (var> max_val) var- = max_val solo usa Un par de instrucciones. <br> Una buena guía para algunos trucos más está aquí: codeproject.com/Articles/6154/…
John U

Aún más importante, los registros auxiliares de E / S mapeados en memoria proporcionan un mecanismo para actualizaciones atómicas. Leer / modificar / escribir puede ir muy mal si se interrumpe la secuencia.
Ben Voigt

1
Tenga en cuenta que todos los registros de puertos se definirán como volatiley, por lo tanto, el compilador no puede realizar ninguna optimización en el código que involucra dichos registros. Por lo tanto, es una buena práctica desensamblar dicho código y ver cómo resultó a nivel de ensamblador.
Lundin

24

El enfoque de campo de bits tiene otras ventajas en el ámbito integrado. Puede definir una estructura que se asigne 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 es MSB primero, pero esto puede depender de la implementación. Además, verifique cómo los manejadores del compilador cruzan los límites de bytes.

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


2
Casi todo lo relacionado con los campos de bits está definido por la implementación. Incluso si logra encontrar todos los detalles sobre cómo su compilador particular los implementa, usarlos en su código seguramente lo hará no portátil.
Lundin

1
@Lundin: es cierto, pero el sistema embebido de bits del sistema (particularmente en los registros de hardware, a lo que se refiere mi respuesta) nunca será útil de todos modos.
Roddy

1
Tal vez no entre CPU completamente diferentes. Pero lo más probable es que desee que sea portátil entre compiladores y entre diferentes proyectos. Y hay muchos "ajustes de bits" incrustados que no están relacionados con el hardware, como la codificación / decodificación de protocolos de datos.
Lundin

... y si tiene la costumbre de usar campos de bits haciendo programación incrustada, encontrará que su código X86 se ejecuta más rápido y más ágil también. No en puntos de referencia simples donde tiene toda la máquina para aplastar el punto de referencia, sino en entornos de tareas múltiples del mundo real donde los programas compiten por los recursos. Ventaja CISC: cuyo objetivo de diseño original era compensar las CPU más rápido que los buses y la memoria lenta.

20

Marque 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 código de máquina SPARC eficiente cuando se compila Sun Studio 8; También lo probé 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.


20

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

2
CHAR_BITya está definido por limits.h, no necesita poner el suyo BITS(y de hecho empeora su código al hacerlo)
MM

14

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

14

Si está haciendo muchos giros, es posible que desee usar máscaras que harán que todo sea más rápido. Las siguientes funciones son muy rápidas y siguen siendo flexibles (permiten girar 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 pequeños procesadores endian que bytes, palabras, dwords, qwords, etc., se asignan correctamente entre sí en la memoria (razón principal de que los pequeños procesadores endian son "mejores" que los procesadores big endian, ah, siento que se acerca una guerra de llamas en...).


2
No use una tabla para una función que pueda implementarse con un solo operador. TQuickByteMask [n] es equivalente a (1 << n). Además, hacer sus argumentos cortos es una muy mala idea. El / y el% será en realidad una división, no un desplazamiento de bits / bit a bit y, porque la división con signo por una potencia de 2 no puede implementarse bit a bit. Debe hacer que el argumento escriba unsigned int!
R .. GitHub DEJA DE AYUDAR AL HIELO

¿Qué sentido tiene esto? ¿Solo hace que el código sea más lento y difícil de leer? No puedo ver una sola ventaja con eso. 1u << n es más fácil de leer para los programadores de C, y con suerte se puede traducir en una instrucción de CPU de un solo reloj. Su división, por otro lado, se traducirá en algo alrededor de 10 tics, o incluso tan mal como hasta 100 ticks, dependiendo de qué tan mal maneje la división la arquitectura específica. En cuanto a la función de mapa de bits, tendría más sentido tener una tabla de búsqueda que traduzca cada índice de bits a un índice de bytes, para optimizar la velocidad.
Lundin

2
En cuanto a big / little endian, big endian asignará enteros y datos sin procesar (por ejemplo, cadenas) de la misma manera: de izquierda a derecha de msb a lsb en todo el mapa de bits. Mientras little endian mapeará enteros de izquierda a derecha como 7-0, 15-8, 23-18, 31-24, pero los datos sin procesar siguen siendo de izquierda a derecha msb a lsb. Entonces, cuán poco endian es mejor para su algoritmo particular está completamente más allá de mí, parece ser lo contrario.
Lundin

2
@R .. Una tabla puede ser útil si su plataforma no puede cambiar eficientemente, como los antiguos microchip mcu's, pero por supuesto, la división en la muestra es absolutamente ineficiente
jeb

12

Utilizar este:

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

    return num;
}

55
Bueno, usa ramificaciones ineficientes.
asdf

3
@asdf El trabajo del compilador es generar el binario más eficiente, el trabajo del programador es escribir código claro
MM

3
Esta es una buena demostración de probar, configurar y borrar un bit en particular. Sin embargo, es un enfoque muy malo para alternar un poco.
Ben Voigt

10

Ampliando la bitsetrespuesta:

#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 desea realizar todas estas operaciones con la programación C en el kernel de Linux , le sugiero que use 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 serán atómicos incluso en computadoras SMP y son útiles para mantener la coherencia entre los procesadores.


9

Visual C 2010, y quizás muchos otros compiladores, tienen soporte directo para operaciones booleanas incorporadas. Un bit tiene dos valores posibles, al igual que un booleano, por lo que podemos usar booleanos en su lugar, incluso si ocupan más espacio que un solo bit en memoria en esta representación. Esto funciona, incluso el sizeof()operador 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);
}

Por lo tanto, a su pregunta, IsGph[i] =1o IsGph[i] =0facilite la configuración y la limpieza de las carpetas.

Para buscar caracteres 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" en este código. Se trata un poco como un número entero, que técnicamente lo es. Un entero de 1 bit que puede contener 2 valores y solo 2 valores.

Una vez utilicé este enfoque para encontrar registros de préstamos duplicados, donde el número de préstamo era la clave ISAM, usando 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 mainframe del que estábamos obteniendo los datos estaba funcionando mal. La simplicidad de las matrices de bits hace que la confianza en su corrección sea muy alta, en comparación con un enfoque de búsqueda, por ejemplo.


std :: bitset es implementado como bits por la mayoría de los compiladores
galinette

@galinette, de acuerdo. El archivo de encabezado #include <bitset> es un buen recurso a este respecto. Además, el vector de clase especial <bool> para cuando necesita cambiar el tamaño del vector. El C ++ STL, 2ª edición, Nicolai M. Josuttis los cubre exhaustivamente en las páginas 650 y 281 respectivamente. C ++ 11 agrega algunas capacidades nuevas a std :: bitset, de especial interés para mí es una función hash en contenedores desordenados. ¡Gracias por el aviso! Voy a eliminar mi comentario de calambre cerebral. Bastante basura en la web. No quiero agregarle nada.

3
Esto utiliza al menos un byte completo de almacenamiento para cada uno bool. Quizás incluso 4 bytes para las configuraciones de C89 que se usan intpara implementarbool
MM

@MattMcNabb, tienes razón. En C ++, el tamaño del tipo int necesario para implementar un booleano no está especificado por el estándar. Me di cuenta de que esta respuesta fue un error hace algún tiempo, pero decidí dejarla aquí, ya que la gente aparentemente la encuentra útil. Para aquellos que quieran usar bits, el comentario de Galinette es de gran ayuda, al igual que mi biblioteca de bits aquí ... stackoverflow.com/a/16534995/1899861

2
@RocketRoy: Probablemente valga la pena cambiar la oración que dice que este es un ejemplo de "operaciones de bit", entonces.
Ben Voigt

6

Utilice uno de los operadores como se define aquí .

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


2
0xes el prefijo para un literal en hexadecimal, no binario.
Ben Voigt

5

Aquí hay algunas macros que uso:

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 utilizada

int value, pos;

valor -
Posición de datos - posición del bit que estamos interesados ​​en establecer, borrar o alternar.

Establecer un poco:

value = value | 1 << pos;

Claro un poco:

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

Alternar un poco:

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

Tipo de retorno de check_nth_bitpuede ser bool.
Xeverous

@ Xeverous sí, depende de la intención de las personas que llaman
Sazzad Hissain Khan

5

Supongamos que algunas cosas primero
num = 55 Integer realizar operaciones bit a bit (set, get, clear, toggle).
n = 4Posición de bit basada en 0 para realizar operaciones bit a bit.

¿Cómo conseguir un poco?

  1. Para obtener el nthbit de desplazamiento a la derecha num num, ntiempos. Luego realice bit a bit Y &con 1.
bit = (num >> n) & 1;

¿Cómo funciona?

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

¿Cómo configurar un poco?

  1. Para establecer un número particular de número. Desplazamiento a la izquierda 1 nveces. Luego realice la |operación OR a nivel de bit con num.
num |= (1 << n);    // Equivalent to; num = (1 << n) | num;

¿Cómo funciona?

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

¿Cómo despejar un poco?

  1. Desplazamiento a la izquierda 1, por nej 1 << n.
  2. Realice un complemento bit a bit con el resultado anterior. De modo que el enésimo bit se desarma y el resto del bit se pone, es decir ~ (1 << n).
  3. Finalmente, realice la &operación AND a nivel de bit con el resultado anterior y num. Los tres pasos anteriores juntos se pueden escribir como num & (~ (1 << n));

Pasos para despejar un poco

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

¿Cómo funciona?

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

¿Cómo alternar un poco?

Para alternar un poco, usamos el ^operador XOR bit a bit . El operador XOR bit a bit evalúa a 1 si el bit correspondiente de ambos operandos es diferente, de lo contrario se evalúa a 0.

Lo que significa alternar un poco, necesitamos realizar la operación XOR con el bit que desea alternar y 1.

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

¿Cómo funciona?

  • Si el bit para alternar es 0, entonces 0 ^ 1 => 1.
  • Si el bit para alternar es 1, entonces 1 ^ 1 => 0.
       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
       0001 0000
     ^ 0011 0111 (55 in decimal)
-----------------
    => 0010 0111 (final result)

Lectura recomendada: ejercicios de operador bit a bit


Gracias por la explicación detallada. Aquí está el enlace para el problema de la práctica de la magia BIT enlace
Chandra Shekhar

4

¿Cómo establece, limpia 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 ocurren cuando numberes un tipo más amplio que 1?
xpuede ser demasiado bueno para el cambio que 1 << xconduce a un comportamiento indefinido (UB). Incluso si xno es demasiado bueno, ~puede que no voltee 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 usarse 1ullo pedantemente (uintmax_t)1y permitir que el compilador se optimice.

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

O emitir, lo que hace que los problemas de codificación / revisión / mantenimiento mantengan el reparto correcto y actualizado.

number |= (type_of_number)1 << x;

O promueva suavemente 1forzando una operación matemática que sea tan ancha 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


¡Una mirada interesante sobre una vieja pregunta! Ni apropiado number |= (type_of_number)1 << x;ni number |= (number*0 + 1) << x;apropiado para establecer el bit de signo de un tipo con signo ... De hecho, tampoco lo es number |= (1ull << x);. ¿Hay alguna forma portátil de hacerlo por posición?
chqrlie

1
@chqrlie IMO, la mejor manera de evitar establecer el bit de signo y arriesgar UB o IDB con cambios es usar tipos sin signo . El código firmado de cambio altamente portátil es demasiado complicado para ser aceptable.
chux - Restablecer Monica

3

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

Este código está roto (Además, ¿por qué tiene ;después de sus definiciones de función?)
Melpomene

@melpomene El código no está roto, lo probé. ¿Quiere decir que no se compilará o que el resultado es incorrecto? Sobre el extra ';' No recuerdo, esos pueden eliminarse de hecho.
Joakim L. Christiansen

(variable & bits == bits)?
melpomene

Gracias por notarlo, se suponía que era((variable & bits) == bits)
Joakim L. Christiansen

uso std::bitseten c ++ 11
pqnet

0

Este programa se basa en la solución anterior de @ Jeremy. Si alguien desea jugar rápidamente.

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

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

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

value << npuede causar un comportamiento indefinido
MM
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.