联合体(共用体) 和 位域 的使用详细解析
联合体(共用体) 和 位域 的使用详细解析
联合体(共用体示例):
该联合体使用了位域
typedef union
{
	uint16_t status;
    struct {
    	uint16_t tick_flag:1,
		sta:2,
		unused:13;
    };
} holding_reg_dev_status_t;
        该联合体包含一个16位的无符号整数status和一个结构体struct,结构体中又包含了三个成员变量:tick_flag、sta和unused。
其中tick_flag为1位,sta为2位,unused为13位。通过使用位域(bit-field),可以将结构体中的各个成员变量存储在不同的位上,节省内存空间。在本例中,tick_flag占用了status中的最低一位(即第0位),sta占用了status中的第1至2位,unused占用了status中的第3至15位。
通过联合体,可以让status和struct共享同一段内存地址,从而实现对同一数据的不同解释。由于status和struct共享同一段内存,因此修改一个变量的值会影响到另一个变量的值。在本例中,可以通过修改status来同时改变struct中各个成员变量的值,也可以通过修改struct中的成员变量来改变status的值。
示例代码如下:
#define TICK_FLAG_MASK 0x0001
#define STA_MASK       0x0006
 
holding_reg_dev_status_t holding_reg_dev_status;
 
void setup()
{
    // 初始化状态变量
    holding_reg_dev_status.status = 0;
    holding_reg_dev_status.tick_flag = 1;
    holding_reg_dev_status.sta = 2;
}
 
void loop()
{
    // 读取并修改状态变量的值
    uint16_t status = holding_reg_dev_status.status;
    status &= ~TICK_FLAG_MASK;  // 清除tick_flag位
    status |= STA_MASK;        // 设置sta位
    holding_reg_dev_status.status = status;
}
在上述示例中,首先通过holding_reg_dev_status.status = 0来初始化状态变量holding_reg_dev_status。然后,在loop()函数中,使用按位与(&)和按位或(|)操作符来读取并修改状态变量的值。通过将TICK_FLAG_MASK取反(即~TICK_FLAG_MASK),再与状态变量进行按位与操作,可以清除tick_flag位。通过将STA_MASK与状态变量进行按位或操作,可以设置sta位。最后,将修改后的status值赋给holding_reg_dev_status.status,从而更新联合体中的各个成员变量的值。
详细解析该联合体:
typedef union
{
	uint16_t status;
    struct {
    	uint16_t tick_flag:1,
		sta:2,
		unused:13;
    };
} holding_reg_dev_status_t;
        这段代码定义了一个联合体 holding_reg_dev_status_t,它包含了一个16位无符号整数 status 和一个结构体。
首先,我们来看结构体部分的定义:
struct 
{ 
    uint16_t tick_flag:1,
    sta:2,
    unused:13; 
};
        该结构体中定义了三个成员变量:tick_flag、sta 和 unused。这些成员变量使用了位域(bit-field)的方式进行定义。
tick_flag 占据了 1 位。
sta 占据了 2 位。
unused 占据了 13 位。
        接下来,我们来看联合体的定义:
typedef union 
{ 
    uint16_t status; 
    struct{ 
        uint16_t tick_flag:1, 
        sta:2, 
        unused:13; 
    }; 
} holding_reg_dev_status_t;
        该联合体包含了一个16位无符号整数 status 和一个匿名结构体。通过联合体的特性,结构体和 status 共享同一段内存空间。
这意味着当我们访问 holding_reg_dev_status_t 类型的变量时,可以选择使用 status 这个整数值,也可以使用结构体中的位域成员变量来访问同一段内存。
例如,假设有一个 holding_reg_dev_status_t 类型的变量 dev_status,我们可以通过以下两种方式访问数据:
使用 status 整数值:
dev_status.status = 0xABCD; // 将整数值赋给 status
uint16_t value = dev_status.status; // 从 status 中读取整数值
2. 使用结构体中的位域成员变量:
dev_status.tick_flag = 1;  // 设置 tick_flag
dev_status.sta = 2;        // 设置 sta
uint16_t value = dev_status.unused;  // 读取 unused 的值
需要注意的是,由于联合体中的结构体使用了位域的方式进行定义,不同编译器可能对位域的实现方式有所不同。因此,在使用位域时需要注意编译器的兼容性和预期的位域布局。
问:struct {
uint16_t tick_flag:1,
sta:2,
unused:13;
};
这里面的一位是几个字节 ???
在这个结构体中,位域的大小是以位(bit)为单位来定义的。一个字节(byte)由8位组成。
根据结构体中的位域定义:
tick_flag 占据了1位。
sta 占据了2位。
unused 占据了13位。
        由于总共占据16位(2个字节),可以将这个结构体看作是一个占据2个字节的位字段(bit-field)。
需要注意的是,位域的位数并不一定要与字节对齐。编译器会按照自己的规则进行位域的布局和对齐,以满足平台的要求。因此,在使用位域时,要注意编译器的实现规则和预期的位域布局。
问:那这样的位怎么赋值呢,赋值的范围怎么判断呢???
对于位域(bit-field)的赋值,我们可以使用点号操作符来访问结构体中的位域成员。例如,在上面的结构体定义中,我们可以使用以下方式对位域进行赋值:
holding_reg_dev_status_t dev_status;
dev_status.tick_flag = 1;   // 将 tick_flag 的值设置为 1
dev_status.sta = 2;         // 将 sta 的值设置为 2
dev_status.unused = 0x7FFF; // 将 unused 的值设置为 0x7FFF
        在进行位域赋值时,需要注意位域的位数和取值范围。例如,在上面的结构体定义中,tick_flag 只占据了一个比特,因此只能取值 0 或 1。同理,sta 占据了两个比特,可以取值 0、1、2 或 3。unused 则占据了 13 个比特,可以取值范围为 0x0000 到 0x7FFF。
需要注意的是,位域的位数不一定要与字节对齐,而是根据编译器的实现规则进行布局。因此,在使用位域时,要注意编译器的实现规则和预期的位域布局。同时,由于位域的实现有一定的灵活性,可能会存在跨平台或跨编译器的兼容性问题,需要谨慎使用。
问:为什么 sta 占据了两个比特,只可以取值 0、1、2 或 3。不能取值其他吗???
对于 sta 这个位域,占据了2个比特(bits),因此可以表示的不同取值是有限的。在这个结构体中,sta 的取值范围被限制在 0、1、2 和 3 这四个值。
这是因为使用2个比特可以表示的不同取值有 2^2 = 4 种可能性,即 0、1、2 和 3。位域的取值范围由位域的位数确定,不能超出这个范围。
在这个例子中,我们可以将不同的 sta 值与相应的含义关联起来,例如:
sta = 0 可以表示某种状态 A。
sta = 1 可以表示某种状态 B。
sta = 2 可以表示某种状态 C。
sta = 3 可以表示某种状态 D。
        这样设计的目的是在有限的位数内表示多个状态,以节省存储空间。然而,如果需要表示更多的状态,就需要增加位域的位数或者其他的数据结构来进行扩展。
        需要注意的是,在使用位域时,位域的位数和取值范围需要根据具体的需求进行设计,并且要确保符合预期的取值范围。
————————————————
                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/jinchi_boke/article/details/135850675
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号