我正在编写一个 C 程序。我想要一个可以作为 char 访问的变量,但我也可以访问其中的特定位。我在想我可以使用这样的工会......
typedef union
{
unsigned char status;
bit bits[8];
}DeviceStatus;
但编译器不喜欢这样。显然你不能在结构中使用位。那么我能做些什么呢?
当然可以,但是您实际上想使用结构来定义这样的位
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;
然后你可以访问DeviceStatus ds;
你可以访问ds.u.bit1
。此外,一些编译器实际上允许您在联合中拥有匿名结构,这样您就可以ds.bit1
在 typedef 中省略 u 时访问。
你有几种可能性。一种是仅使用布尔数学来获取位:
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...
另一种是使用位域:
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...
第一个是(至少可以说)更便携,但是当你处理状态位时,很可能代码甚至没有一点可移植性,所以你可能不太在乎......
正如已经说过的,你不能在 C 中寻址小于一个字节的内存。我会写一个宏:
#define BIT(n) (1 << n)
并使用它来访问位。这样,无论您访问的结构的大小如何,您的访问权限都是相同的。您可以将代码编写为:
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)
}
这使您可以接近您提议的系统,该系统将使用 [] 而不是 ()。
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;
在 C 中可寻址的最小单元始终是一个字节(char
在 C 中称为)。您不能直接访问位。访问位的最接近的方法是定义一个名为的数据类型bitpointer
并为其定义一些函数或宏:
#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;
}
我建议不要使用联合,因为它是由实现定义的,它们是填充 msb-to-lsb 还是 lsb-to-msb(参见 ISO C99,6.7.2.1p10)。
您可以通过将位放在联合内的结构中来做到这一点,但它可能会或可能不会起作用,具体取决于您的实现。语言定义没有指定单独的位以什么顺序与unsigned char
; 的位匹配。更糟糕的是,它甚至不能保证这些位将与 重叠unsigned char
(编译器可能决定将单独的位放在单词的最高有效侧和unsigned char
最低有效侧,反之亦然)。
在您的情况下,通常的技术是使用按位运算。定义以位的含义命名的常量,例如,
#define FLAG_BUSY 0x01
#define FLAG_DATA_AVAILABLE 0x02
#define FLAG_TRANSMISSION_IN_PROGRESS 0x04
...
#define FLAG_ERROR 0x80
然后读取和写入各个位:
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 */