c++ - type - bit fields c
Cómo leer/escribir bits arbitrarios en C/C++ (8)
Suponiendo que tengo un byte b con el valor binario de 11111111
¿Cómo puedo, por ejemplo, leer un valor entero de 3 bits comenzando en el segundo bit o escribir un valor entero de cuatro bits comenzando en el quinto?
"¿Cómo puedo, por ejemplo, leer un valor entero de 3 bits comenzando en el segundo bit?"
int number = // whatever;
uint8_t val; // uint8_t is the smallest data type capable of holding 3 bits
val = (number & (1 << 2 | 1 << 3 | 1 << 4)) >> 2;
(Supuse que el "segundo bit" es el bit # 2, es decir, el tercer bit realmente).
Más de dos años o más después de haber formulado esta pregunta, me gustaría explicarlo de la manera en que me gustaría que se explicara cuando aún era un novato completo y sería más beneficioso para las personas que desean comprender el proceso.
En primer lugar, olvide el valor de ejemplo "11111111", que no es realmente adecuado para la explicación visual del proceso. Entonces, deje que el valor inicial sea 10111011
(187 decimal) que será un poco más ilustrativo del proceso.
1 - cómo leer un valor de 3 bits a partir del segundo bit:
___ <- those 3 bits
10111011
El valor es 101, o 5 en decimal, hay 2 maneras posibles de obtenerlo:
- máscara y cambio
En este enfoque, los bits necesarios primero se enmascaran con el valor 00001110
(14 decimal) después de lo cual se desplaza en su lugar:
___
10111011 AND
00001110 =
00001010 >> 1 =
___
00000101
La expresión para esto sería: (value & 14) >> 1
- cambio y máscara
Este enfoque es similar, pero el orden de las operaciones se invierte, lo que significa que el valor original se desplaza y luego se enmascara con 00000111
(7) para dejar solo los últimos 3 bits:
___
10111011 >> 1
___
01011101 AND
00000111
00000101
La expresión para esto sería: (value >> 1) & 7
Ambos enfoques implican la misma cantidad de complejidad y, por lo tanto, no difieren en el rendimiento.
2 - cómo escribir un valor de 3 bits a partir del segundo bit:
En este caso, se conoce el valor inicial, y cuando este es el caso en el código, puede encontrar una forma de establecer el valor conocido a otro valor conocido que utiliza menos operaciones, pero en realidad esto rara vez es el caso, la mayoría de las veces el código no conocerá ni el valor inicial, ni el que se escribirá.
Esto significa que para que el nuevo valor se "empalme" con éxito en byte, los bits de destino se deben establecer en cero, después de lo cual el valor desplazado se "empalma" en su lugar, que es el primer paso:
___
10111011 AND
11110001 (241) =
10110001 (masked original value)
El segundo paso es cambiar el valor que queremos escribir en los 3 bits, digamos que queremos cambiarlo de 101 (5) a 110 (6)
___
00000110 << 1 =
___
00001100 (shifted "splice" value)
El tercer y último paso es empalmar el valor original enmascarado con el valor de "empalme" desplazado:
10110001 OR
00001100 =
___
10111101
La expresión para todo el proceso sería: (value & 241) | (6 << 1)
(value & 241) | (6 << 1)
Bonificación: cómo generar las máscaras de lectura y escritura:
Naturalmente, usar un convertidor binario a decimal está lejos de ser elegante, especialmente en el caso de los contenedores de 32 y 64 bits: los valores decimales se vuelven locos. Es posible generar fácilmente las máscaras con expresiones, que el compilador puede resolver de manera eficiente durante la compilación:
- leer máscara para "máscara y desplazamiento":
((1 << fieldLength) - 1) << (fieldIndex - 1)
, suponiendo que el índice en el primer bit es 1 (no cero) - leer la máscara para "shift and mask":
(1 << fieldLength) - 1
(el índice no juega un rol aquí ya que siempre se desplaza al primer bit - máscara de escritura: simplemente invierta la expresión de máscara "máscara y desplazamiento" con el operador
~
¿Cómo funciona (con el campo de 3 bits que comienza en el segundo bit de los ejemplos anteriores)?
00000001 << 3
00001000 - 1
00000111 << 1
00001110 ~ (read mask)
11110001 (write mask)
Los mismos ejemplos se aplican a los enteros más amplios y al ancho y posición de los bits arbitrarios de los campos, variando en consecuencia los valores de desplazamiento y máscara.
También tenga en cuenta que los ejemplos asumen un entero sin signo, que es lo que quiere usar para usar enteros como alternativa portátil de campo de bits (los campos de bits regulares no están garantizados en modo alguno por el estándar para ser portátil), tanto el desplazamiento a la izquierda como a la derecha inserte un relleno 0, que no es el caso con el desplazamiento a la derecha de un entero con signo.
Aún más fácil:
Usando este conjunto de macros (pero solo en C ++ ya que depende de la generación de funciones miembro):
#define GETMASK(index, size) (((1 << (size)) - 1) << (index))
#define READFROM(data, index, size) (((data) & GETMASK((index), (size))) >> (index))
#define WRITETO(data, index, size, value) ((data) = ((data) & (~GETMASK((index), (size)))) | ((value) << (index)))
#define FIELD(data, name, index, size) /
inline decltype(data) name() { return READFROM(data, index, size); } /
inline void set_##name(decltype(data) value) { WRITETO(data, index, size, value); }
Podrías buscar algo tan simple como:
struct A {
uint bitData;
FIELD(bitData, one, 0, 1)
FIELD(bitData, two, 1, 2)
};
Y tienen los campos de bit implementados como propiedades a las que puede acceder fácilmente:
A a;
a.set_two(3);
cout << a.two();
Reemplace decltype
con gcc''s typeof
pre-C ++ 11.
Necesita cambiar y enmascarar el valor, por lo que, por ejemplo ...
Si quieres leer los primeros dos bits, solo tienes que enmascararlos así:
int value = input & 0x3;
Si desea compensarlo, necesita desplazar N bits correctos y luego enmascarar los bits que desea:
int value = (intput >> 1) & 0x3;
Para leer tres bits como lo hizo en su pregunta.
int value = (input >> 1) & 0x7;
Para leer bytes use std :: bitset
const int bits_in_byte = 8;
char myChar = ''s'';
cout << bitset<sizeof(myChar) * bits_in_byte>(myChar);
Para escribir necesita usar operadores de bits tales como & ^ | Y << >>. asegúrese de aprender lo que hacen.
Por ejemplo, para tener 00100100, necesita establecer el primer bit en 1 y cambiarlo con los operadores << >> 5 veces. si quiere continuar escribiendo, simplemente continúe configurando el primer bit y cámbielo. es muy parecido a una vieja máquina de escribir: escribes y cambias el papel.
Para 00100100: configure el primer bit en 1, desplace 5 veces, establezca el primer bit en 1 y cambie 2 veces:
const int bits_in_byte = 8;
char myChar = 0;
myChar = myChar | (0x1 << 5 | 0x1 << 2);
cout << bitset<sizeof(myChar) * bits_in_byte>(myChar);
Si continúa capturando bits de sus datos, es posible que desee utilizar un campo de bits. Simplemente tendrá que configurar una estructura y cargarla con solo unos y ceros:
struct bitfield{
unsigned int bit : 1
}
struct bitfield *bitstream;
luego más tarde, cárguelo así (reemplazando char con int o cualquier dato que esté cargando):
long int i;
int j, k;
unsigned char c, d;
bitstream=malloc(sizeof(struct bitfield)*charstreamlength*sizeof(char));
for (i=0; i<charstreamlength; i++){
c=charstream[i];
for(j=0; j < sizeof(char)*8; j++){
d=c;
d=d>>(sizeof(char)*8-j-1);
d=d<<(sizeof(char)*8-1);
k=d;
if(k==0){
bitstream[sizeof(char)*8*i + j].bit=0;
}else{
bitstream[sizeof(char)*8*i + j].bit=1;
}
}
}
Luego accede a los elementos:
bitstream[bitpointer].bit=...
o
...=bitstream[bitpointer].bit
Todo esto está asumiendo que están trabajando en i86 / 64, no en el brazo, ya que el brazo puede ser grande o poco endian.
Tienes que hacer una operación de cambio y máscara (Y). Sea b cualquier byte y p el índice (> = 0) del bit del que desea tomar n bits (> = 1).
Primero tienes que cambiar a la derecha b por p veces:
x = b >> p;
Segundo, debes enmascarar el resultado con n :
mask = (1 << n) - 1;
y = x & mask;
Puedes poner todo en una macro:
#define TAKE_N_BITS_FROM(b, p, n) ((b) >> (p)) & ((1 << (n)) - 1)
solo usa esto y feelfree:
#define BitVal(data,y) ( (data>>y) & 1) /** Return Data.Y value **/
#define SetBit(data,y) data |= (1 << y) /** Set Data.Y to 1 **/
#define ClearBit(data,y) data &= ~(1 << y) /** Clear Data.Y to 0 **/
#define TogleBit(data,y) (data ^=BitVal(y)) /** Togle Data.Y value **/
#define Togle(data) (data =~data ) /** Togle Data value **/
por ejemplo:
uint8_t number = 0x05; //0b00000101
uint8_t bit_2 = BitVal(number,2); // bit_2 = 1
uint8_t bit_1 = BitVal(number,1); // bit_1 = 0
SetBit(number,1); // number = 0x07 => 0b00000111
ClearBit(number,2); // number =0x03 => 0b0000011
int x = 0xFF; //your number - 11111111
¿Cómo puedo, por ejemplo, leer un valor entero de 3 bits que comienza en el segundo bit?
int y = x & ( 0x7 << 2 ) // 0x7 is 111
// and you shift it 2 to the left