文章详情页 您现在的位置是:网站首页>文章详情

C语言结构体中的位字段

图片丢失 WBK 发表于:2019年5月8日 17:10 分类:【嵌入式软件 4339次阅读

C语言结构体中有一个用起来很方便的东西,叫位字段,可以定义每一个变量的位的大小。

一般在嵌入式软件设计中,很多数据命令的寄存器有固定的位数,可以用位字段来解析和赋值

C Primer Plus书中的位字段内容如下:

位字段是一个signed int或unsigned int类型变量中的一组相邻的位(C99和C11新增了_Bool类型的位字段)。位字段通过一个结构声明来建立,该结构声明为每个字段提供标签,并确定该字段的宽度。例如,下面的声明建立了一个4个1位的字段:

struct {
    unsigned int autfd : 1;
    unsigned int bldfc : 1;
    unsigned int undln : 1;
    unsigned int itals : 1;
}prnt;

根据该声明,prnt包含4个1位的字段。现在,可以通过普通的结构成员运算符(.)单独给这些字段赋值:

prnt.itals = 0;
prnt.undln = 1;

由于每个字段恰好为1位,所以只能为其赋值1或0。变量prnt被储存在int大小的内存单元中,但是在本例中只使用了其中的4位。


带有位字段的结构提供一种记录设置的方便途径。许多设置(如,字体的粗体或斜体)就是简单的二选一。例如,开或关、真或假。如果只需要使用 1 位,就不需要使用整个变量。内含位字段的结构允许在一个存储单元中储存多个设置。


有时,某些设置也有多个选择,因此需要多位来表示。这没问题,字段不限制 1 位大小。可以使用如下的代码:

struct {
    unsigned int code1 : 2;
    unsigned int code2 : 2;
    unsigned int code3 : 8;
} prcode;

以上代码创建了两个2位的字段和一个8位的字段。可以这样赋值:

prcode.code1 = 0;
prcode.code2 = 3;
prcode.code3 = 102;

但是,要确保所赋的值不超出字段可容纳的范围。


如果声明的总位数超过了一个unsigned int类型的大小会怎样?会用到下一个unsigned int类型的存储位置。一个字段不允许跨越两个unsigned int之间的边界。编译器会自动移动跨界的字段,保持unsigned int的边界对齐。一旦发生这种情况,第1个unsigned int中会留下一个未命名的“洞”。


可以用未命名的字段宽度“填充”未命名的“洞”。使用一个宽度为0的未命名字段迫使下一个字段与下一个整数对齐:

struct {
    unsigned int field1    : 1 ;
    unsigned int         : 2 ;
    unsigned int field2    : 1 ;
    unsigned int         : 0 ;
    unsigned int field3    : 1 ;
}stuff;

这里,在stuff.field1和stuff.field2之间,有一个2位的空隙;stuff.field3将储存在下一个unsigned int中。


字段储存在一个int中的顺序取决于机器。在有些机器上,存储的顺序是从左往右,而在另一些机器上,是从右往左。另外,不同的机器中两个字段边界的位置也有区别。由于这些原因,位字段通常都不容易移植。尽管如此,有些情况却要用到这种不可移植的特性。例如,以特定硬件设备所用的形式储存数据。


在实际的使用中举例如下:

比如说Spi的数据解析,可以用到 共用体 和 位字段 。

比如说Spi的数据诊断返回值如下:

image.png

那么可以如下定义:

typedef union
{
    uint32 DiagData;
    struct
    {
        uint32 R_W      : 1 ;
        uint32 MSG_ID   : 5 ;
        uint32 S2G_0    : 1 ;
        uint32 S2G_TST0 : 1 ;
        uint32 S2B_0    : 1 ;
        uint32 S2B_TST0 : 1 ;
        uint32 OL_OFF0  : 1 ;
        uint32 OL_ON0   : 1 ;
        uint32 S2G_1    : 1 ;
        uint32 S2G_TST1 : 1 ;
        uint32 S2B_1    : 1 ;
        uint32 S2B_TST1 : 1 ;
        uint32 OL_OFF1  : 1 ;
        uint32 OL_ON1   : 1 ;
        uint32 S2G_2    : 1 ;
        uint32 S2G_TST2 : 1 ;
        uint32 S2B_2    : 1 ;
        uint32 S2B_TST2 : 1 ;
        uint32 OL_OFF2  : 1 ;
        uint32 OL_ON2   : 1 ;
        uint32 S2G_3    : 1 ;
        uint32 S2G_TST3 : 1 ;
        uint32 S2B_3    : 1 ;
        uint32 S2B_TST3 : 1 ;
        uint32 OL_OFF3  : 1 ;
        uint32 OL_ON3   : 1 ;
        uint32 ENL      : 1 ;
        uint32 RBL      : 1 ;
    }DataAnalysis;
}FaultDiagnostic;

将Spi接收的反馈数据赋值给

FaultDiagnostic.DiagData

就可以很方便的进行解析数据。

此定义用于芯片采用大端存储的方式,如果芯片采用的是小段存储,那么顺序需要倒置。





版权声明 本文属于本站  原创作品,文章版权归本站及作者所有,请尊重作者的创作成果,转载、引用自觉附上本文永久地址: http://blog.lujianxin.com/x/art/ak7d80qr8ciy

上一篇:C语言中的联合体Union

下一篇:敏捷开发

文章评论区

作者名片

图片丢失
  • 作者昵称:WBK
  • 原创文章:3篇
  • 转载文章:1篇
  • 加入本站:2043天

站点信息

  • 运行天数:2047天
  • 累计访问:164169人次
  • 今日访问:0人次
  • 原创文章:69篇
  • 转载文章:4篇
  • 微信公众号:第一时间获取更新信息