看runtime源码时,看到如下声明变量的,变量后分号前加冒号和数字": 数字"即为位域操作。
uintptr_t indexed : 1;
1个字节包含8位,有些变量保存的数据不需要占用这么长的空间(比如bool类型,只有两个状态true和false, 1位就可以搞定,剩下的7位就浪费了),这就催生了“位域”结构,位域将1个字节划分成不同的区域,每个区域都有个位域名(可以理解为变量名,上边的代码中位域名为indexed),程序员可以代码通过位域名访问其中的数据。
一. 声明
类型说明符 位域名:位域长度;
位域结构体,我理解是一种特殊的结构体,其成员变量都是位域,声明如下
struct 位域结构名
{
类型说明符 位域名:位域长度;
类型说明符 位域名:位域长度;
类型说明符 位域名:位域长度;
...
类型说明符 位域名:位域长度;
};
二. 基本原则
1. 位域变量的长度不能大于其类型的长度(sizeof(类型) * 8)
变量char has_assoc的类型位char 1字节 8位,小于定义的10,所以编译器警告
2. 不能用于位域字段的操作:取地址操作符&,取偏移量操作
位域是若干位空间,是没有地址的
3. 位域可以是无名位域,无名位域只能用作填充或调整位置,不能使用。如下图结构体S007的最后一个变量就是无名位域,无法使用
4. 位域字段不能声明为类的静态成员
5. 位域结构体的大小必须是其最长基本类型大小的整数倍(sizeof(类型) * 8)
三. 内存分配规则
1. 判断大小端,以我自己的机子为例,不同环境下有可能不同
定义联合体U007和位域结构体S007,将U007实例u7的number赋值31,二进制为 | 0000 0000 | 0000 0000 | 0000 0000 | 0001 1111 |,根据存储原则,数值都是从高地址往低地址读,因此位域结构体S007中的位域变量是从低地址开始分配内存的,即
2. 位域变量类型相同
位域变量长度之和小于[sizeof(变量类型)*8], 则后面的位域字段将紧邻前一个字段存储, 直到不能容纳为止
位域变量长度之和大于[sizeof(变量类型)*8], 则后面的位域字段将从下一个存储单元的起始地址处开始存放(其偏移量恰好为sizeof(变量类型)的整数倍)
位域结构体S007的3个成员变量都是unfigned short类型,sizeof(unfigned short)*8 = 16位,
a0占1位,从起始地址分配1位;
a1占9位,a0+a1=10位,小于16位(sizeof(unfigned short)*8),因此a1在a0后连续分配9位;
a2占15位,(a0 + a1) + a2 = 25位,大于16位,因此跳过6位,在S007起始地址偏移量为16位的地方,给a2分配15位。
再次提醒:小端是分配内存地址时从低地址开始,但是变量的数值是从高地址往低地址读
3. 位域变量类型不同时,各个编译器的具体实现有差异,VC6采取不压缩方式,GCC和Dev-C++都采用压缩方式,以下是我自己机子的处理方式
位域结构体S007的前2个成员变量unfigned char类型(sizeof(unfigned char)*8 = 8位),后一个成员变量unfigned short(sizeof(unfigned short)*8 = 16位)
a占2位,从起始地址分配2位;
b占3位,a+b=5位,小于8位(sizeof(unfigned char)*8,b的类型是unfigned char),因此b在a后连续分配3位;
c占15位,(a + b) + c = 20位,大于16位(sizeof(unfigned short)*8,c的类型是unfigned short),因此跳过11位,在S007起始地址偏移量为16位的地方,给c分配15位。
有上图可以看出,在我的机子上,如果位域变量的类型不同,仍会进行内存压缩(如果需要跳位,判断哪个位域变量,就用该变量的类型进行偏移量对齐判断)
4. 如果位域变量之间穿插着非位域变量, 则不进行压缩
非位域变量也可以理解为特殊的位域变量,只不过占的位数是变量长度,即 类型 位域名 : sizeof(类型)*8
根据规则3,则无法进行内存压缩
位域结构体S007的第2个成员变量char tmp;是非位域变量,转成位域变量为 char tmp:8;
a占2位,从起始地址分配2位;
tmp占8位,a+tmp=10位,大于8位(sizeof(char)*8,tmp的类型是char),因此跳过6位,在S007起始地址偏移量为8位的地方,给tmp分配8位;
b占3位,在tmp后给b分配3位。
5. 上边举的例子中参数都是unsigned标识的,如果带上符号位会是什么情况呢?
关于带符号基本类型变量在内存中存储方式,请看这里http://www.cnblogs.com/xieyajie/p/8125214.html
位域结构体S008定义了2个带符号位域变量,a0占2位,a1占3位
a0是带符号short类型,内存中保存的是补码11,首位符号位为1,负数,推出源码为11,转为十进制为-1;
a1是带符号short类型,内存中保存的是补码001,首位符号位为0,正数,源码为001,转为十进制为1。
如果a0只占1位会怎么样?从测试结果可以看出,这1位几十符号位,也是数值位