views:

84

answers:

6

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?

+2  A: 

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.

torak
In C99 bitfields should only be `signed` or `unsigned` `int` or `_Bool`.
Jens Gustedt
C99 6.7.2.1/10: "An implementation may allocate any addressable storage unit large enough to hold a bit-field." Allocating just one `char` isn't required, so it's possible that no aliasing occurs at all. Also, anonymous structures within the union are required by the standard.
Potatoswatter
+1  A: 
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;
Jon Hanna
Does the C99 spec define a size for the _Bool type? I had a quick look and coundn't see anything obvious.
torak
@torak: By definition, any type is at least as wide as a `char`. But this is not the point here since these are bitfields. The compiler actually *could* place the fields `bitX` over the bits of status, but there is no guarantee for it.
Jens Gustedt
You can use any integral type of _Bool for the type of a bit-field as long as it has at least that number of bits. Since the size used here is 1bit, there's no bit-field-allowed type that won't serve.
Jon Hanna
+3  A: 

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

Jerry Coffin
Doesn't `unsigned` correspond to a larger data type than a `char`? This would make `sizeof(char) < sizeof(DeviceStatus)`, which could cause some problems.
torak
@Jerry Coffin: Only your first version is guaranteed to use the bits of status. In the `union`, the individual `bitX` fields might be padded as pleases to the compiler.
Jens Gustedt
@torak: Yes, the could. If that's a problem for you, then you'd probably want to make the fields all `unsigned char` instead.
Jerry Coffin
@Jens: Yes, that's why I said that first is "arguably more portable". OTOH, the second works with enough compilers that it's still worth mentioning (at least IMO).
Jerry Coffin
@torak: the size of `unsigned` is irrelevant since the type is used in a bit field; bit fields must be declared as `int` (with optional signedness) anyway (or `_Bool` in C99). But a compiler could decide that `struct bits` takes more than one byte, and in fact `sizeof(struct bits)` = `sizeof(int)` with gcc.
Gilles
@torak, note the `: 1` which will mean that only one bit of storage is used for the value. It restricts the type to that number of bits, so as long as you don't use a number bigger than the size of the type you use, it's fine.
Jon Hanna
A: 

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

Roland Illig
A: 

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 */
Gilles
+1  A: 

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

reemrevnivek