数据结构 -- 位域

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构 -- 位域相关的知识,希望对你有一定的参考价值。

参考技术A

一个字节有8位,在存储时有些数据并不需要占用一个完整的字节,只需要占用一个或几个二进制位即可。例如开关只有通电和断电两种状态,用 0 和 1 表示足以,也就是用一个二进位。基于这种考虑, C 语言提供了『 位域 』这个数据结构。

在结构体定义时,我们可以指定某个成员变量所占用的二进制位数(Bit),这就是位域。来看下面的例子:

当限制了成员的位数时,如果给成员赋值超过其位数,则会导致数据溢出。来看下面的例子:

上面定义了结构体变量a,包含一个非位域成员m,以及两个位域成员n、ch。分别进行两次赋值并输出,输出结果如下所示:

第一次赋值的输出结果分析:

第二次赋值的输出结果分析:

位域的储存规则如下:

位域实质上是含有位域成员的结构体,所以除了遵循这个储存规则外,还需要遵循结构体分内存对齐规则,接下来通过如下示例来分析位域的存储逻辑。

示例1:

输出结果分析:

因此,bs总共需要22位内存,即3字节,而根据结构体对齐规则,bs的内存必须为其最大成员类型长度(int)的整数倍,所以最终输出为4字节。

示例2:

输出结果分析:

因此,bs总共需要86位的内存空间,即11字节,而根据结构体对齐规则,bs的内存必须为其最大成员类型长度(int)的整数倍,所以最终输出为12字节。

示例3:

输出结果分析:

因此,bs需要152位的内存,即19字节,经由结构体内存对齐,需要是4字节的倍数,所以最终输出为20字节。

1. 数据结构 -- 结构体Struct
2. 数据结构 -- 共用体Union

C位域操作

位域的概念

1个字节包含8位,有些变量保存的数据不需要占用这么长的空间(比如bool类型,只有两个状态true和false, 1位就可以搞定,剩下的7位就浪费了),这就催生了“位域”结构,位域将1个字节划分成不同的区域,每个区域都有个位域名,程序员可以代码通过位域名访问其中的数据。

位域的声明

类型 位域名:位域长度;

位域结构体,我理解是一种特殊的结构体,其成员变量都是位域,声明如下

struct 位域结构名 

  类型说明符 位域名:位域长度;
  ...
  类型说明符 位域名:位域长度;
;

基本原则

现有一个简单的结构体

typedef struct Test1

    char a:1;
    int b:2;
test1;

1. 位域变量的长度不能大于其类型的长度(sizeof(类型) * 8)

技术图片

变量char has_assoc的类型位char 1字节 8位,小于定义的10,所以编译器警告

2.  不能用于位域字段的操作:取地址操作符&,取偏移量操作

位域是若干位空间,是没有地址的

技术图片

3. 位域可以是无名位域,无名位域只能用作填充或调整位置,不能使用。

typedef struct Test1

    char a:1;
    int b:2;
    int :1;
test1;

4. 位域字段不能声明为类的静态成员

5. 位域结构体的大小必须是其最长基本类型大小的整数倍(sizeof(类型) * 8)

6.如果一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如: 

struct bs     
     
    unsigned a:4     
    unsigned :0 /*空域*/     
    unsigned b:4 /*从下一单元开始存放*/     
    unsigned c:4     

这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占用4位,c占用4位。

7.不够一个类型的size时,将按其中最大的那个类型对齐。

struct foo4 
    char    a : 2;
    char    b : 3;
    int c : 1;
;

foo4中虽然三个位域所占用空间之和为6 bit < 8 bit(1 byte),但是由于char和int的对齐系数是不同的,是不能捆绑在一起,那是不是a、b捆绑在一起按照char对齐,c单独按照int对齐呢?我们 打印一下sizeof(struct foo4)发现结果为8,也就是说编译器把a、b、c一起捆绑起来并以int做对齐了。

8.如果位域字段之间穿插着非位域字段,则不进行压缩;

typedef struct Test1

    char a:1;
    int b:2;
test1;
//运行结果:4
typedef struct Test1

    char a:1;
    int b:2;
    long c;
test1;
//运行结果:16

总结:

1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止
2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,GCC采取压缩方式;
4) 如果位域字段之间穿插着非位域字段,则不进行压缩;
5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。

以上是关于数据结构 -- 位域的主要内容,如果未能解决你的问题,请参考以下文章

c语言 关于位域的使用

C语言结构体中冒号的作用——位域

关于位域在结构体的应用

位域(位段)

C 语言编程 — 构造数据类型 — 位域(bit field)

C 语言编程 — 构造数据类型 — 位域(bit field)