有没有办法通过联合访问各个位?
我正在写一个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
。 此外,一些编译器实际上允许您在联合中使用匿名结构,这样如果从typedef中省略u,则只能访问ds.bit1
。
你有几种可能性。 一种是使用布尔数学来获取位:
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中可寻址的最小单元始终是一个字节(在C中称为char
)。 你不能直接访问。 获取访问位的最接近方法是定义一个名为bitpointer
的数据类型,并bitpointer
定义一些函数或宏:
#include 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)。
您可以通过将这些位放在union中的结构中来实现,但它可能会也可能不会起作用,具体取决于您的实现。 语言定义没有指定单独的位将与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 */