Un campo de bits es una estructura de datos que se asigna a uno o más bits adyacentes que han sido asignados para propósitos específicos, de modo que cualquier bit o grupo de bits dentro de la estructura se puede configurar o inspeccionar. [1] [2] Un campo de bits se utiliza más comúnmente para representar tipos integrales de ancho de bit fijo conocido, como los booleanos de un solo bit .
El significado de los bits individuales dentro del campo lo determina el programador; por ejemplo, el primer bit de un campo de bits (ubicado en la dirección base del campo ) a veces se utiliza para determinar el estado de un atributo particular asociado con el campo de bits. [3]
En las CPU y otros dispositivos lógicos, se utilizan habitualmente conjuntos de campos de bits denominados indicadores para controlar o indicar el resultado de operaciones concretas. [4] Los procesadores tienen un registro de estado compuesto por indicadores. Por ejemplo, si el resultado de una suma no se puede representar en el destino, se establece un desbordamiento aritmético . Los indicadores se pueden utilizar para decidir operaciones posteriores, como instrucciones de salto condicional . Por ejemplo, una instrucción (Saltar si es igual) en el lenguaje ensamblador x86 dará como resultado un salto si el indicador Z (cero) se estableció mediante alguna operación anterior.JE ...
Un campo de bits se distingue de una matriz de bits en que esta última se utiliza para almacenar un conjunto grande de bits indexados por números enteros y a menudo es más amplia que cualquier tipo integral admitido por el lenguaje. [ cita requerida ] Los campos de bits, por otro lado, normalmente caben dentro de una palabra de máquina , [3] y la denotación de bits es independiente de su índice numérico. [2]
Los campos de bits se pueden utilizar para reducir el consumo de memoria cuando un programa requiere una cantidad de variables enteras que siempre tendrán valores bajos. Por ejemplo, en muchos sistemas, almacenar un valor entero requiere dos bytes (16 bits) de memoria; a veces, los valores que se van a almacenar en realidad necesitan solo uno o dos bits. Tener varias de estas pequeñas variables compartiendo un campo de bits permite un empaquetamiento eficiente de los datos en la memoria. [5]
En C, los campos de bits definidos por la implementación nativa se pueden crear utilizando int
, [a] unsigned int
, signed int
, _Bool
(en C99), _BitInt(N)
, unsigned _BitInt(N)
(en C23) u otros tipos definidos por la implementación. En C++, se pueden crear utilizando cualquier tipo integral o de enumeración; la mayoría de los compiladores de C también lo permiten. En este caso, el programador puede declarar una estructura para un campo de bits que etiquete y determine el ancho de varios subcampos. [6] Los campos de bits declarados adyacentes del mismo tipo pueden luego ser empaquetados por el compilador en un número reducido de palabras, en comparación con la memoria utilizada si cada 'campo' se declarara por separado.
En el caso de lenguajes que carecen de campos de bits nativos, o en los que el programador desea controlar la representación de bits resultante, es posible manipular manualmente los bits dentro de un tipo de palabra más grande. En este caso, el programador puede configurar, probar y cambiar los bits en el campo utilizando combinaciones de operaciones de enmascaramiento y operaciones bit a bit . [7]
Declaración de un campo de bits en C y C++ : [6]
// opaco y mostrar #define SI 1 #define NO 0// estilos de línea #define SÓLIDO 1 #define PUNTO 2 #define DISCO 3// colores primarios #define AZUL 0b100 #define VERDE 0b010 #define ROJO 0b001// colores mezclados #define NEGRO 0 #define AMARILLO (ROJO | VERDE) /* 011 */ #define MAGENTA (ROJO | AZUL) /* 101 */ #define CIAN (VERDE | AZUL) /* 110 */ #define BLANCO (ROJO | VERDE | AZUL) /* 111 */const char * colores [ 8 ] = { "Negro" , "Rojo" , " Verde" , "Amarillo" , "Azul" , "Magenta" , "Cian" , "Blanco" }; // propiedades del campo de bits struct BoxProps { unsigned int opaque : 1 ; unsigned int fill_color : 3 ; unsigned int : 4 ; // rellenar hasta 8 bits unsigned int show_border : 1 ; unsigned int border_color : 3 ; unsigned int border_style : 2 ; unsigned char : 0 ; // rellenar hasta el byte más cercano (16 bits) unsigned char width : 4 , // Dividir un byte en 2 campos de 4 bits height : 4 ; };
La disposición de los campos de bits en C struct
está definida por la implementación . Para que el comportamiento siga siendo predecible entre compiladores, puede ser preferible emular los campos de bits con un operador primitivo y de bits:
/* Cada una de estas directivas del preprocesador define un solo bit, correspondiente a un botón del controlador. El orden de los botones coincide con el del Nintendo Entertainment System. */ #define KEY_RIGHT 0b00000001 #define KEY_LEFT 0b00000010 #define KEY_DOWN 0b00000100 #define KEY_UP 0b00001000 #define KEY_START 0b00010000 #define KEY_SELECT 0b00100000 #define KEY_B 0b01000000 #define KEY_A 0b10000000carácter sin signo gameControllerStatus = 0 ; /* Establece el gameControllerStatus usando OR */ void KeyPressed ( unsigned char key ) { gameControllerStatus |= key ; } /* Borra el gameControllerStatus usando AND y ~ (NO binario)*/ void KeyReleased ( unsigned char key ) { gameControllerStatus &= ~ key ; } /* Prueba si un bit está configurado usando AND */ unsigned char IsPressed ( unsigned char key ) { return gameControllerStatus & key ; }
El registro de estado de un procesador es un campo de bits que consta de varios bits de bandera. Cada bit de bandera describe información sobre el estado actual del procesador. [8] Como ejemplo, el registro de estado del procesador 6502 se muestra a continuación:
Estos bits son configurados por el procesador después del resultado de una operación. Algunos bits (como los indicadores Carry, Interrupt-disable y Decimal) pueden controlarse explícitamente mediante instrucciones set y clear. Además, también se definen instrucciones de bifurcación para modificar la ejecución en función del estado actual de un indicador.
Por ejemplo, después de una ADC
instrucción (Add with Carry), la BVS
instrucción (Branch on oVerflow Set) se puede usar para saltar en función de si el indicador de desbordamiento fue configurado por el procesador después del resultado de la instrucción de adición.
Se puede extraer un subconjunto de indicadores en un campo de indicadores mediante la operación AND con una máscara . Una gran cantidad de lenguajes admiten el operador de desplazamiento1 << n
(<<), donde alinea un solo bit a la posición n. La mayoría también admite el uso del operador AND (&) para aislar el valor de uno o más bits.
Si el byte de estado de un dispositivo es 0x67 y el quinto bit de bandera indica que los datos están listos, el byte de máscara es 2^5 = 0x20
. Al combinar el byte de estado 0x67 ( 0110 0111
en binario) con el byte de máscara 0x20 ( 0010 0000
en binario), se obtiene 0x20. Esto significa que el bit de bandera está configurado, es decir, que el dispositivo tiene datos listos. Si el bit de bandera no se hubiera configurado, se habría evaluado como 0, es decir, que no hay datos disponibles del dispositivo.
Para comprobar el bit n de una variable v , realice una de las siguientes acciones: (ambas son equivalentes)
bool nth_is_set = ( v & (1 << n )) != 0;bool nth_se_establece = ( v >> n ) & 1;
La escritura, lectura o alternancia de bits en los indicadores se puede realizar únicamente mediante las operaciones OR, AND y NOT, operaciones que se pueden realizar rápidamente en el procesador. Para establecer un bit, se realiza una operación OR entre el byte de estado y un byte de máscara. Cualquier bit establecido en el byte de máscara o en el byte de estado se establecerá en el resultado.
Para alternar un bit, utilice la operación XOR entre el byte de estado y el byte de máscara. Esto activará un bit si está borrado o desactivará un bit si está activado.