【问题标题】:Please clarify my understanding on bit fields and Unions in C请澄清我对 C 中位域和联合的理解
【发布时间】:2016-02-10 06:03:58
【问题描述】:

我正在研究微控制器驱动程序文件,但无法理解联合和位域的正确使用

typedef union
{
     uint8 U;                          //Unsigned 
     sint8 I;                          //signed 
     Register_x_Bits B;                //Bit field access 
}u1;

typedef struct _Register_x_Bits        //8 bit register size 
{
    uint8 Field1:1;                    //endianness is maintained for all registers      
    uint8 Field2:1;                
    uint8 Field3:1;                    
    uint8 Field4:1;                   
    uint8 Field5:4;    
}Register_x_Bits;


u1 reg;

reg.U = ReadRegister();                 // data is initially being read from the register 

//based on the previous data  a bit value needs to be changed in particular location of the data 

reg.B.Field3 = 0;

WriteRegister(reg.U);

这就是我难以理解的地方

1) 通过仅更改reg.B.Field3 位字段值,它会仅更改一个特定位(在本例中为字段 3)还是清除寄存器中的所有先前数据并用新的reg.B.Field3 填充它

许多教程指出Unions 中只有一个活跃成员

2) 我可以更改同一个寄存器中的多个位域吗? 我可以通过上面相同的方法更改它们并保持剩余的完整吗

reg.B.Field1 =0;
reg.B.Field3 =1;
reg.B.Field4 =1100;

修改

让整个寄存器值为1111 0 1 0 1,现在如果我更改Fields 1,3,4 会影响Fields 0,2 中的值吗?

【问题讨论】:

  • 您必须先定义Register_x_Bits,然后才能在联合定义中使用它。显示的代码不应编译。 (但您可以轻松地颠倒并集和结构的顺序,以便编译。)
  • 您不能将所有十进制 1100 放入 4 位字段中。你有没有想过 6 作为值? (1100 % 16 是 12,这是您编写的代码所得到的。)
  • 分配给reg.B.Field3 不会改变reg.B 结构中的其他位。它会影响reg.Ireg.U 中的值——实际上,一位会被重置,但哪一位是实现定义的(大多数位字段行为是实现定义的,所以你必须查看实现手册——和如果您拥有的手册中尚未记录该信息,请索取该信息)。
  • Register_x_Bits 结构中是否存在拼写错误,因为Field4 输入了两次?
  • @oysteijo .. 哦,我没注意到……这是一个错字。我现在更正了

标签: c cpu-registers unions bit-fields


【解决方案1】:

好的先看看下面的代码

#include <stdio.h>
typedef struct _BITS{
    unsigned char f1:1;
    unsigned char f2:1;
    unsigned char f3:1;
    unsigned char f4:1;
    unsigned char f5:4;

}BITS;
typedef union {
    unsigned char a;
    char b;
    BITS c;
}u1;

int main(void) {
    u1 reg;
    reg.a = 255;
    reg.c.f1 = 0;
    //reg.c.f2 = 1;
    printf("%d", reg.c.f1);
    printf("%d", reg.c.f2);
    return 0;
}

为了模仿ReadRegister(),我写了reg.a = 255。所以现在当我写reg.c.f1 = 0 时,如果它改变了所有的位,那么reg.c.f2 也会打印 0。但它不会发生。位域仅针对该特定位,而不是整个字节。因此不,它只会清除那一点

现在,如果我写 reg.a = 0reg.c.f1 = 255 并且如果它设置所有位,那么 reg.c.f2 将产生 1 但它不会。因此不,你不能设置位域的所有位

编辑 但是,您可以通过其他方式设置多个位。请参阅@sdao 的答案

希望能回答你的问题

【讨论】:

  • 我在我的问题中添加了一个寄存器位描述..如果我有reg.a= 255 然后更改reg.c.f1 = 0reg.c.f4 = 0 将我的reg.a 更改为11110110
  • 您需要再次回顾一下unions 的概念。这里2分..1。如果你改变一个位,其他位不会改变..2。如果使用位域更改一位,则整个字节值都会受到影响。因为 1. 每个位都与其他位不同,并且 2. 在union 内存是共享的,这里只创建和共享一个变量。通过一个所做的更改将反映在另一个中。
  • 如果你觉得有用请选择我的答案。
【解决方案2】:

1:您可以单独更改字段而不影响其他字段
2:可以同时更改多个字段。

仅供参考:在您的情况下,可以为每个字段设置的值是[0: sizeof(unsigned char)],但每个字段可以接收的实际值取决于它的位数,所以要小心

假设您有 N 位字段,您将 M 分配给该字段,那么将设​​置的实际值是:(M &amp; (N^2 - 1)) 其中(N^2 - 1) 是掩码.

例如:

reg.B.Field1 = 10; // actual value: reg.B.Field1 = 0 (because 10 = b'0000 1010, Field1 is 1 bit field then the mask is `1^2 - 1 = 1 (b'1)`), 10 & 1 = 0)
reg.B.Field3 = 7;  // actual value: reg.B.Field3 = 1 (because 7 = b'0000 0111, Field3 is 1 bit field then the mask is `1^2 - 1 = 1 (b'1)`, 7 & 1 = 1)
reg.B.Field4 = 24; // actual value: reg.B.Field4 = 8 (because 24 = b'0001 1000, Field4 is 4 bits field then the mask is `4^2 - 1 = 15 (b'1111)`, 24 & 15 = 8)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-09-10
    • 1970-01-01
    • 1970-01-01
    • 2011-01-20
    • 1970-01-01
    • 1970-01-01
    • 2011-11-03
    相关资源
    最近更新 更多