C语言-结构体中的冒号:位字段

位字段定义

位字段:一个int或者unsigned int类型变量中的一组相邻的位。
位字段通过一个结构声明来建立,该结构声明为每个字段提供标签,并确定该字段的宽度。不同于一般结构体的是它在定义成员的时候需要指定成员所占的位数。

位字段用途

带有位字段的结构提供一种记录设置的方便途径。许多设置(如,字体的粗体或者斜体)就是简单的二选一。例如,开或关、真或假。如果只需要使用1位,就不需要使用整个变量。内含位字段的结构允许在一个存储单元中储存多个设置。它主要用于一些使用空间很宝贵的程序设计中,如嵌入式程序设计。

使用单个位例子

例如,下面的结构体声明建立了一个 4个1位的字段。

//结构体PRNT的声明中,包含了4个1位的字段。
struct PRNT
{
    unsigned int autfd : 1;  //占用1位
    unsigned int bldfc : 1;  //占用1位
    unsigned int undln : 1;
    unsigned int itals : 1;
} obj;

//结构体赋值操作,现在可以通过普通的结构成员运算符(.)单独给这些字段赋值。
obj.itals = 0;  
obj.undln = 1;  //可以为0,可以为1,因声明了一位只有两种选择

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

使用多个位例子

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

//创建两个2位字段和一个8位字段的结构体
struct PRCODE
{
    unsigned int code1 : 2;  //支持二进制的四个数:00,01,10,11
    unsigned int code2 : 2;
    unsigned int code3 : 8;
}prcode;

//赋值方式
prcode.code1 = 0;
prcode.code1 = 3;
prcode.code1 = 102;

声明总位数超出问题

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

超出解决办法

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

struct STUFF
{
    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中的顺序取决于机器。在有些机器上,存储的顺序是从左往右的,而另一些机器,是从右往左的。另外,不同的机器中两个字段边界的位置也有区别。使用位字段定义的数据不能在不同字节顺序的机器之间移动。由于这些原因,位字段通常都不容易移植
对于位字段中的成员不能用位操作符进行运算,因为它们没有地址。
尽管如此,有些情况却要用到这种不可移植的特性。例如,以特定硬件设备所用的形式储存数据。

位字段示例

#include <stdio.h>

#define true    1
#define false   0

#define SOLID   0   //实线
#define DOTTED  1   //点线
#define DASHED  2   //虚线

//三原色
#define BLUE    4   //蓝
#define GREEN   2   //绿
#define RED     1   //红

//混合色
#define BLACK   0               //黑色
#define YELLOW  (RED | GREEN)   //黄色
#define MAGENTA (RED | BLUE)    //洋红色
#define CYAN    (GREEN | BLUE)  //蓝绿色、青绿色
#define WHITE   (RED | GREEN | BLUE)    //白色

typedef unsigned int uint;

const char * colors[8] = {"black", "red", "green", "yellow", "blue", "magenta", "cyan", "white"};

typedef struct Box_Props
{
    uint opaque : 1;
    uint fill_color : 3;
    uint : 4;
    uint show_border : 1;
    uint border_color : 3;
    uint border_style : 2;
    uint : 2;
} box_props;

void show_settings(const box_props * pb);

int main()
{
    //插件并初始化 box_props结构
    box_props box = {true, YELLOW, true, GREEN, DASHED};

    printf("Original box setting ");
    show_settings(&box);
    box.opaque = false;
    box.fill_color = WHITE;
    box.border_color = MAGENTA;
    box.border_style = SOLID;
    printf("\nModified box settings:\n");
    show_settings(&box);

    return 0;
}

void show_settings(const box_props * pb)
{
    printf("Box is %s.\n", pb->opaque == true ? "opaque" : "transpatent");
    printf("The file color is %s.\n", colors[pb->fill_color]);
    printf("Border %s.\n", pb->show_border == true ? "shown" : "not shown");
    printf("The border color is %s.\n", colors[pb->border_color]);
    printf("The border style is ");
    switch(pb->border_style)
    {
    case SOLID: 
        printf ("solid.\n"); 
        break;
    case DOTTED: 
        printf ("dotted.\n"); 
        break; 
    case DASHED: 
        printf ("dashed.\n"); 
        break;
    default:    
        printf ("unknown type.\n");
    }
}

下面是该程序的输出:

Original box setting:
Box is opaque.
The file color is yellow.
Border shown.
The border color is green.
The border style is dashed.

Modified box settings:
Box is transpatent.
The file color is white.
Border shown.
The border color is magenta.
The border style is solid.

参考

  1. 位字段-百度百科
  2. c prime plus (第6版) 中文版 15.4 位字段
  3. C语言中的位字段 - caianye的专栏

C语言书籍资料

C语言书籍资源汇总-优快云下载

### C语言结构体冒号的作用——域 在C语言中,结构体中的冒号用于定义**域**(bit-field),这是一种特殊的字段类型。它允许程序员指定某个变量所占的比特数,从而实现更精细的数据存储控制[^1]。 #### 什么是域? 域是一种数据结构成员的形式,在这种形式下,可以显式地规定该成员占据多少个比特(bit)。通过这种方式,可以在内存有限的情况下优化空间利用率。例如: ```c struct Example { unsigned int flag : 1; // 占用1个比特 unsigned int value : 7; // 占用7个比特 }; ``` 在此例子中,`flag`仅占用1个比特,而`value`则占用7个比特。这意味着整个结构体可能只需要单字节的空间来存储这两个成员[^2]。 #### 定义方式 当在一个结构体内声明一个成员并附加冒号以及随后的一个整数值时,就创建了一个域。语法如下所示: ```c type member_name : width; ``` 其中: - `type` 是标准C支持的基本数据类型之一(如int、char等),通常推荐使用无符号类型以避免潜在的符号扩展问题。 - `member_name` 表示这个域的名字;如果不需要名字,则可以用匿名域来进行填充或对齐调整。 - `width` 则指定了此成员能够使用的具体比特数量[^3]。 #### 存储布局与边界对齐 需要注意的是,不同编译器对于如何安排这些域的具体置可能存在差异,并且它们可能会受到目标平台架构的影响。比如某些情况下,即使总宽度小于等于一字节大小也可能分配超过一字符长度的实际物理存储单元[^4]。 另外值得注意的一点是在访问域的时候不能直接获取它的地址因为它是嵌套于更大的基本单之中并非独立存在的实体除非借助特定方法间接处理像下面这样尝试取得_a地址的操作将会引发错误: ```c printf("%d\n", sizeof(struct A)); char *p = &(s._a); // 错误:无法取域的地址 ``` 这是因为 `_a`, `_b`, 和其他类似的项实际上只是更大整体的一部分而不是单独可寻址的对象。 ### 总结 综上所述,C语言里的结构体冒号主要用来设置各个字段各自对应的宽即所谓的'域'. 这种机制有助于节约内存资源尤其适用于硬件驱动程序开发等领域当中.
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值