位段bit segment
早期C语言中没有该标准,后期才加入
位段出现的契机
操作系统中常常要区分开某些文件/用户等的权限,例如一个文件是否有修改权限、删除权限,又比如一个用户是否有添加、删除、修改、查询文件的权限,操作系统都是需要鉴别的
我们假设,使用宏来代表权限:第几个位置为1,表示有哪个权限
#define ADD 1 // 添加的权限 0001
#define DEL 2 // 删除的权限 0010
#define EDT 4 // 修改的权限 0100
#define QUE 8 // 查询的权限 1000
基于此我们可以通过位运算来完成权限的识别与赋予
int _tmain(int argc,TCHAR argv[])
{
// 用于表示权限
// 逻辑或运算 0100 | 0010 = 0110 表示当前具有 修改和删除 权限
int nPrivileges = EDT | DEL;
// 增加权限
// 0110 | 0001 = 0111 表示当前具有 修改、删除和添加的权限
nPrivileges = nProvileges | ADD
// 删除权限
// 逻辑与运算 0111 & 1101 = 0101 删除掉 删除的权限
// 由于逻辑与运算是都为1才是1,有一个为0就为0,所以~DEL则代表删除的一位肯定为0,所以&后,nPrivileges该位也肯定为0
nPrivileges = nPrivileges & ~DEL
// 判断是否具有某权限
// 按位求与判断是否具有添加权限
// =0代表没有该权限 !=0代表具有该权限
// 0101 & 0001 由于 & 0001 而和的规矩是都为1才为1,所以结果中前三位一定为 0 , 若不具有该权限则最后一个 & 后也为 0 ,所以结果 =0 代表没有该权限
if ( nPrivileges & ADD )
{
puts("具有添加权限");
}
return 0;
}
位段bit segment
为了方便的行驶与上述相同的功能,引入一个新的语法:位段
struct tagPrivileges
{
// : 后面的数字代表长度
int add : 1; // add长度为1位
int edt : 1;
int del : 1;
int que : 1;
}
此时若再想实现上面的权限赋予、权限增加、权限删除、权限判断,可以这么做:
🔥 起始底层还是通过上述的位运算来完成,只不过这一次是交给编译器来完成int _tmain(int argc,TCHAR argv[])
{
struct tagPrivileges Pri;
//赋予权限
Pri.add = 1;
Pri.del = 1
//删除权限
Pri.del = 0
printf("%x\r\n",Pri.add);
}
此时printf会输出什么呢? 期待结果应为 1
这显然不是我们期待的结果
整型数的位数拓展
对于上述例子,我们前期在结构体中定义的add只占1位,而输出时为%x输出4字节,所以就势必会发生位数的拓展
有符号数的拓展
规则:有符号数做长度拓展时,高位添符号位
对于有符号位,拓展出的多的部分填符号位,这样就不会改变原数的符号性质,整的依旧为正,负的依旧为负
此时对于Pri.add由于前期我们定义其为有符号整型,所以拓展时会将拓展部份填充符号位,又因为其是一个正数,所以符号位为1,所以高位全部填1
所以最终输出的记过就是 FFFFFFFF
无符号数的拓展
规则:高位全部填0
所以我们定义为:
struct tagPrivileges
{
unsigned int add : 1;
unsigned int edt : 1;
unsigned int del : 1;
unsigned int que : 1;
};
即可
幺蛾子时刻
现在对于标志位的定义有以下要求:
QQQDDEEEEA
struct tagPrivileges
{
unsigned int add : 1;
unsigned int edt : 4;
unsigned int del : 2;
unsigned int que : 3;
}
int _tmain(int argc,TCHAR argv[])
{
struct tagPrivileges Pri = {0};
Pri.add = 6; // add明显放不下6位 6 = 110
Pri.del = 1;
return 0;
}
此时内存中会是什么样子呢?
QQQDDEEEEA
-
Q没有定义,所以为000DDEEEEA -
D定义为1,所以为00001EEEEA -
E没有定义,所以为000010000A -
A定义为6,也就是二进制的110,此时由于A只占1位,所以我们只取A的最低位也就是 00000100000对应内存中16进制的就是20H
无名位段
struct tagPrivileges
{
unsigned int add : 1;
unsigned int edt : 4;
unsigned int : 3; // 这三位没有名字,无法引用,但就占3位
unsigned int del : 2;
unsigned int que : 3;
}
// QQQDD???EEEEA
🔥 **该位置不会被前后占用**
跨对齐位段
struct tagPrivileges
{
unsigned int add : 1;
unsigned int edt : 4;
unsigned int : 0;
unsigned int del : 2;
unsigned int que : 3;
}
// QQQDDEEEEA
此时构成跨对齐位段,特点:
此时add、edt、?(int : 0)要一共占满4字节(占满一个对齐值4字节,接下来的东西都下一个对齐值开始放),再开始放del、和que
所以对于 int : 0 的作用就是用0和前面的位数补齐一个对齐值的字节数,再开始存放接下来的位段
所以其形式为:
QQQDD???????????????????????????EEEEA (32 - 5 = 27个?)
0001000000000000000000000000000000001 占 8 字节
示例
定义一个浮点编码
struct IEEE
{
unsigned int data : 23; // 数据位
unsigned int exp : 8; // 指数位
unsigned int sig : 1; // 符号位
};
int _tmain(int argc,TCHAR argv[])
{
float f = 3.14f;
struct IEEE *p = (IEEE *)&f;
int n1 = p->data; //就可以方便的取出浮点数的各位了
int n2 = p->exp;
int n3 = p->sig ;
return 0;
}