structs - ¿Hay una manera de acceder a bits individuales con una unión?
unions c programming (6)
Estoy escribiendo un programa en C. Quiero una variable a la que pueda acceder como un char pero también puedo acceder a los bits específicos de Estaba pensando que podría usar una unión como esta ...
typedef union
{
unsigned char status;
bit bits[8];
}DeviceStatus;
Pero al compilador no le gusta esto. Aparentemente no puedes usar bits en una estructura. Entonces, ¿qué puedo hacer en su lugar?
Claro, pero realmente quieres usar una estructura para definir los bits como este
typedef union
{
struct
{
unsigned char bit1 : 1;
unsigned char bit2 : 1;
unsigned char bit3 : 1;
unsigned char bit4 : 1;
unsigned char bit5 : 1;
unsigned char bit6 : 1;
unsigned char bit7 : 1;
unsigned char bit8 : 1;
}u;
unsigned char status;
}DeviceStatus;
Entonces puedes acceder para DeviceStatus ds;
puede acceder a ds.u.bit1
. Además, algunos compiladores realmente le permitirán tener estructuras anónimas dentro de una unión, de manera que solo puede acceder a ds.bit1
si omite u de la typedef.
Como ya se ha indicado, no puede direccionar la memoria más pequeña que un byte en C. Yo escribiría una macro:
#define BIT(n) (1 << n)
y úsalo para acceder a los bits. De esa manera, su acceso es el mismo, independientemente del tamaño de la estructura a la que acceda. Usted escribiría su código como:
if (status & BIT(1)) {
// Do something if bit 1 is set
} elseif (~status | BIT(2) {
// Do something else if bit 2 is cleared
} else {
// Set bits 1 and 2
status |= BIT(1) | BIT(2)
// Clear bits 0 and 4
status &= ~(BIT(0) | BIT(4))
// Toggle bit 5
status ^= BIT(5)
}
Esto le permite acceder a su sistema propuesto, que usaría [] en lugar de ().
La unidad más pequeña que es direccionable en C es siempre un byte (llamado char
en C). No se puede acceder un poco directamente. La forma más cercana de acceder a los bits sería definir un tipo de datos llamado punto de bitpointer
y definir algunas funciones o macros para ellos:
#include <stdbool.h>
typedef struct bitpointer {
unsigned char *pb; /* pointer to the byte */
unsigned int bit; /* bit number inside the byte */
} bitpointer;
static inline bool bitpointer_isset(const bitpointer *bp) {
return (bp->pb & (1 << bp->bit)) != 0;
}
static inline void bitpointer_set(const bitpointer *bp, bool value) {
unsigned char shifted = (value ? 1 : 0) << bp->bit;
unsigned char cleared = *bp->pb &~ (1 << bp->bit);
*(bp->pb) = cleared | shifted;
}
Recomiendo contra las uniones porque está definido por la implementación si se llenan de msb a lsb o de lsb a msb (consulte ISO C99, 6.7.2.1p10).
Puede hacerlo colocando los bits en una estructura dentro de la unión, pero puede o no funcionar, dependiendo de su implementación. La definición del lenguaje no especifica en qué orden se unirán los bits separados con los bits del unsigned char
; peor aún, ni siquiera garantiza que los bits se superpongan con el unsigned char
(el compilador puede decidir colocar los bits separados hacia el lado más significativo de una palabra y el unsigned char
hacia el lado menos significativo o viceversa).
La técnica habitual en su situación es utilizar operaciones bitwise. Defina constantes nombradas según el significado de los bits, por ejemplo,
#define FLAG_BUSY 0x01
#define FLAG_DATA_AVAILABLE 0x02
#define FLAG_TRANSMISSION_IN_PROGRESS 0x04
...
#define FLAG_ERROR 0x80
Luego leer y escribir bits individuales:
if (status & FLAG_BUSY) ... /* test if the device is busy */
status &= ~FLAG_ERROR; /* turn off error flag */
status |= FLAG_TRANSMISSION_IN_PROGRESS /* turn on transmission-in-progress flag */
Tienes un par de posibilidades. Uno sería usar solo matemáticas booleanas para llegar a los bits:
int bit0 = 1;
int bit1 = 2;
int bit2 = 4;
int bit3 = 8;
int bit4 = 16;
int bit5 = 32;
int bit6 = 64;
int bit7 = 128;
if (status & bit1)
// whatever...
Otra es usar los campos de bits:
struct bits {
unsigned bit0 : 1;
unsigned bit1 : 1;
unsigned bit2 : 1;
// ...
};
typedef union {
unsigned char status;
struct bits bits;
} status_byte;
some_status_byte.status = whatever;
if (status_byte.bits.bit2)
// whatever...
El primero es (al menos posiblemente) más portátil, pero cuando estás tratando con bits de estado, es probable que el código no sea ni siquiera un poco portátil, por lo que es posible que no te importe mucho eso ...
typedef union
{
unsigned char status;
struct bitFields
{
_Bool bit0 : 1;
_Bool bit1 : 1;
_Bool bit2 : 1;
_Bool bit3 : 1;
_Bool bit4 : 1;
_Bool bit5 : 1;
_Bool bit6 : 1;
_Bool bit7 : 1;
} bits;
}DeviceStatus;