Question

I am writing a C program. I want a variable that I can access as a char but I can also access the specific bits of. I was thinking I could use a union like this...

typedef union 
{
    unsigned char status;
    bit bits[8];
}DeviceStatus;

but the compiler doesn't like this. Apparently you can't use bits in a structure. So what can I do instead?

Was it helpful?

Solution

Sure, but you actually want to use a struct to define the bits like this

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;

Then you can access for DeviceStatus ds; you can access ds.u.bit1. Also, some compilers will actually allow you to have anonymous structures within a union, such that you can just access ds.bit1 if you ommit the u from the typedef.

OTHER TIPS

You have a couple of possibilities. One would be to just use Boolean math to get at the 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...

Another is to use bitfields:

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

The first is (at least arguably) more portable, but when you're dealing with status bits, chances are that the code isn't even slightly portable anyway, so you may not care much about that...

As has already been stated, you can't address memory smaller than a byte in C. I would write a macro:

#define BIT(n) (1 << n)

and use it to access the bits. That way, your access is the same, regardless of the size of the structure you're accessing. You would write your code as:

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

This gets you access close to your proposed system, which would use [] instead of ().

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;

The smallest unit that is addressable in C is always a byte (called char in C). You cannot access a bit directly. The closest way to get to accessing bits would be to define a data type called bitpointer and define some functions or macros for it:

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

I recommend against unions because it is implementation-defined whether they are filled msb-to-lsb or lsb-to-msb (see ISO C99, 6.7.2.1p10).

You can do it by putting the bits in a struct inside the union, but it may or may not work, depending on your implementation. The language definition does not specify in what order the separate bits will be matched with the bits of the unsigned char; worse, it doesn't even guarantee that the bits will overlap with the unsigned char (the compiler might decide to place the separate bits towards the most significant side of a word and the unsigned char towards the least significant side or vice versa).

The usual technique in your situation is to use bitwise operations. Define constants named after the meaning of the bits, e.g.,

#define FLAG_BUSY 0x01
#define FLAG_DATA_AVAILABLE 0x02
#define FLAG_TRANSMISSION_IN_PROGRESS 0x04
...
#define FLAG_ERROR 0x80

Then to read and write individual bits:

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 */
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top