可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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?
回答1:
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.
回答2:
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...
回答3:
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 ().
回答4:
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;
回答5:
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).
回答6:
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 */