C语言学习笔记---位字段

   在C语言中通常操作只有真假两种状态的的数据时使用布尔bool变量比较多,如果需要同时观察多个状态,这时候选择位操作效率会更高,用一个字节的8位分别表示8种状态。比较节省内存,处理起来效率更高。但是这种方法有一个缺点,就是看起来不直观,比如0x5C要想知道每一位的状态还得换算一下,同时要操作某一个单独位时,还必须使用位操作运算,比如位与、位或、异或。

   如果熟悉单片机的就会想到,能不能在C语言中像操作寄存器那样直接操作一个字节的单独一位呢?
在这里插入图片描述
   比如这是一个单片机的端口方向配置寄存器,每一个端口有8个口,每个口可以单独配置为输出模式或者输入模式。比如现在设置 PA_DDR.DDR3 = 1;PA_DDR.DDR6 = 0; 那么就代表此时将PA口的端口3设置为输出模式,端口6设置为输入模式。用这种方式操作时,比直接使用位操作看起来更加的直观明了。

   在C语言中也有这也得一种操作方式,它叫做 位字段 ,位字段是一个 signed int 或 unsigned int 类型变量中的一组相邻位。为字段通过一个结构声明来建立,该结构声明为每个字段提供标签,并确定该字段的宽度。

struct
{
    unsigned int Sun: 1;
    unsigned int Mon: 1;
    unsigned int Tue: 1;
    unsigned int Wed: 1;
    unsigned int Thur: 1;
    unsigned int Fri: 1;
    unsigned int Sat: 1;
} week;

   在上面的这个 星期的结构中,提供了7个1位的字段,每一个字段就代表其中一天。这个就可以通过结构成员运算符点( . )来对每一个字符赋值。

		week.Mon = 1;
        week.Tue = 1;
        week.Wed = 1;
        week.Thur = 1;
        week.Fri = 1;
        week.Sat = 0;
        week.Sun = 0;

   通过结构运算符设置周一到周五的值为1,周末值为0。通过这样的方式可以单独操作字节中的某一位,和单片机中对于寄存器的操作就很类似了。由于每个字段只占了一位,所以字符的值只能是0或者1。还可以将每个字段设置为多个位。

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

  创建了一个2位字段,一个3位字段,一个8位字段。可以这样赋值:

prcode.code1 = 3;
prcode.code2 = 7;
prcode.code3 =  255;

  赋值的大小不能超过字段所能表示的最大范围。

  在第一个例子中,字段的所有位数加起来只有7位,但是在第二个例子中,所有的字段位数加起来有13位,那么在内存中这两种情况是如何存储的呢?

  在C语言中规定,如果声明的总位数超过了一个unsigned int 类型的大小,就会用到下一个unsigned int 类型的存储位置,一个字段不允许跨越两个unsigned int之间的边界,编译器会自动移动跨界的字段。保持unsigned int的边界对齐。

  为了方便观察内存中的数据情况,将代码移植到单片机中。来观察在内存中的存储情况。

struct
{
    unsigned int Sun: 1;
    unsigned int Mon: 1;
    unsigned int Tue: 1;
    unsigned int Wed: 1;
    unsigned int Thur: 1;
    unsigned int Fri: 1;
    unsigned int Sat: 1;
} week;

struct
{
    unsigned int code1 : 5;
    unsigned int code2 : 5;
    unsigned int code3 : 7;
} prcode;

		week.Sun = 1;
		week.Mon = 0;
        week.Tue = 1;
        week.Wed = 0;
        week.Thur = 1;
        week.Fri = 0;
        week.Sat = 1;
       
      
        prcode.code1 = 0x03;
        prcode.code2 = 0x11;
        prcode.code3 =  0x22;

        cnt1 = sizeof(week);
        cnt2 = sizeof(prcode);

  给两个位字段的每一段分别赋值,最后通过sizeof()计算着两个位字段占用内存的大小。
在这里插入图片描述
  week每个字段赋值完成后,内存中的数据变成了55。
在这里插入图片描述
  字段Sun为最低位,字段Sat为最高位。
  接下来存储prcode字段的数据。
在这里插入图片描述
  存储完 0x03和0x11时,内存中的数据变成了02 23.
在这里插入图片描述
  code1字段和code1字段各占了5位,所以最低5位是code1的值,紧接着5位是code2的值。其余位默认为0。

在这里插入图片描述
  最后给code3字段赋值,code3占7位,值为0x22。
在这里插入图片描述
  最后计算出week占用2个字节,prcode占用4个字节。
在这里插入图片描述
  当在内存中编译器自动边界对齐后,就会留下一个未命名的“洞”,可以使用未命名的字段宽度填充未命名的“洞”。如下代码:

struct
{
    unsigned int code1 : 1;
    unsigned int       : 4;
    unsigned int code2 : 1;
    unsigned int       : 1;
    unsigned int code3 : 1;
} prcode;

  在code1和code2之间有4个空洞,在code2和code3之间有1个空洞。
在这里插入图片描述
  内存中的数据是 a1 。
在这里插入图片描述
  可以看到“空洞”的位置被自动填充为0。在C语言中规定,位字段是以unsigned int作为基本的布局单元,即使一个结构的成员是1位字段,在内存中该结构的大小也是一个unsigned int类型的大小。另外在位字段中布尔类型 (bool)也是可以使用的。

  • 1
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值