C位字段简单总结

About…
C里面的位字段十分有用和强大,想位操作符一样,也是一种直接对数据位operator的一种方法。在《C Primer Plus》一书中,对位字段的解释十分具体和彻底。这里主要对书中内容做一些摘要和总结。

位字段!What?
位字段(bit field)是一个unsigned int或signed int中一组相邻的位。位字段由一组声明结构建立,该结构声明为每个字段提供标签,并且决定字段的宽度。个人理解就是把一个int型拆成以‘位’位单位的字段来用。

建立位字段的几种格式

struct box_props {
    unsigned int opaque             : 1;
    unsigned int fill_color         : 3;
    unsigned int                    : 4;
    unsigned int show_border        : 1;
    unsigned int border_color       : 3;
    unsigned int border_style       : 2;
    unsigned int                    : 2;
    unsigned int                    : 0;
};

上面的代码定义格式并不统一。
(1)unsigned int opaque : 1;建立了一个宽度为1位(已经是最小了!)的字段,并且他的标签是opaque(就是name,以后可以通过它来使用这个位);
(2)unsigned int fill_color : 3;这个类比第一个就是宽度变大了,是占3个bit的字段。
(3)unsigned int : 4;这个有些奇怪。竟然没有tag,那怎么用?当然这个是没法用的,只是为了占4个位置(bit)。有时候你定义了28个字段,但是一个int型通常是32位,这时候就会在这个int内存中留下一个4位的‘洞’,是不是就可以用这种定义填充呢!
(4)unsigned int : 0;这个更奇怪,连宽度都没有是什么意思?! 可以这样想:一个int是4个字节组成,也就是32位,加入你定义了一系列的字段,当字段的累计宽度超过了32位,这个时候就应该有一种处理的方式:那将会使用下一个unsigned int的存储位置。不允许一个字段跨越两个unsigned int之间的边界。编译器会自动的移位一个这样的字段定义,使字段按unsigned int边界对齐。使用一个宽度为0的未命名的字段迫使下一个字段与下一个整数对齐!

十分注意!
(引自原文)一个重要的机器依赖性是将字段放置到一个int中的顺序。在有些机器上,这个顺序是从左向右,另一些机器上是从右向左。不同机器在两个字段间边界位置上也有区别。由于这些原因,位字段往往难以移植!典型的,把他们用于不可移植的用途,例如按照某个特定硬件设备所使用的确切格式来存放数据。

一个凸显位字段应用优势的代码示例
位字段占用空间十分小,并且取值只有0 和 1,这就可以用来做许多事,例如你可以规定0是关,1是开,那么他就可以当一个开关。又如在设计一个窗体的时候,是否透明、二选一的style等都是只需要两个状态标志,那么位字段会使得存储的这些数据更加紧凑。另外,在选择窗体的颜色、边框颜色等虽然有多种颜色,依旧可以用稍宽一点的位字段来标记(如有8中颜色那么可以定义宽度为3的位字段:000、001、010、011、100、101、110、111)没错!就是数组电路那一套东西,这不就是C和底层如此接近的原因吗!
窗体设计代码演示:

/*
*C的位字段使用实例
*借助位字段设计一个窗体的属性盒子
*/
#include <stdio.h>
#include <stdlib.h>

//是否透明可见
#define YES     1
#define NO      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)
const char* colors[8] = { "black", "red", "green", "yellow",
"blue", "magenta", "cyan", "white" };
//基于位字段的窗体属性结构体
struct box_props {
    unsigned int opaque             : 1;
    unsigned int fill_color         : 3;
    unsigned int                    : 4;
    unsigned int show_border        : 1;
    unsigned int border_color       : 3;
    unsigned int border_style       : 2;
    unsigned int                    : 2;
};

//函数声明
void show_settings(const struct box_props *pb);

int main()
{
    //创建和初始化属性盒
    struct box_props box = {YES, YELLOW, YES, GREEN, DASHED};

    printf("原始属性盒子的设置是:\n");
    show_settings(&box);

    //通过位字段修改属性盒子并输出
    box.opaque = NO;
    box.fill_color = WHITE;
    box.border_color = MAGENTA;
    box.border_style = SOLID;

    printf("更新后的属性盒子设置是:\n");
    show_settings(&box);

    system("pause");
    return 0;
}

void show_settings(const struct box_props *pb) {
    printf("Box is %s.\n", 
        pb->opaque == YES ? "opaque" : "transparent");
    printf("The fill color is %s.\n", colors[pb->fill_color]);
    printf("Border %s.\n", 
        pb->show_border == YES ? "shown" : "notshown");
    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"); break;
    }
}

启示
从代码中很明显可以看出位字段与普通变量的用法很相似:

初始化位字段结构体和初始化普通结构体一样。
可以为位字段赋值。
可以把位字段作为switch语句的表达式。
位字段可以作为数组的下标索引。
位的编号是从0开始的,和数组的索引性质一般。

把位字段和位运算符用好,对一些算法的实现很有帮助!但是二者在实现同一目的的过程,差别是巨大的,在不同的情况下使用不同的方式对于书写代码有很大的帮助。希望大家可以有所收获!

参考书目:《C Primer Plus》第五版

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值