前言
在之前工作中从未使用过这种形式,可能是一个公司一个编码风格吧,现在的公司里面大量代码定义时候都使用到了位域,作者在网上搜了一点资料看了一下,记录一下自己简单的理解。
位域简介
在信息存储时,有时我们很确切的知道某个值的范围,我们就可以使用位域来限制这个变量的取值范围,并且能节省内存。使用位域时
变量类型 变量 : 数值;
的意思是该变量仅占用数值对应值的比特位个数。并且数值对应值不应大于变量类型所占的最大比特位数。
例子
比如一个在结构体中有个值a取值范围是0-15那么我们在定义变量类型的时候就可以使用位域,使用位域可以节省内存空间,bc等值同理,为了方便理解多写几个值。写法如下
typedef struct
{
unsigned short a:4; //定义无符号字符形a ,a仅使用0-3比特位
unsigned short b:8; //定义无符号短整形b ,b仅使用4-11比特位
unsigned :12; //定义无符号整形起到占位作用,占12-23比特位
unsigned int c:2; //定义无符号整形c ,c仅使用24-25比特位
...
}Test;
int main(int argc, char *argv[])
{
Test b;
b.a=15;
printf("%d\n",b.a); //输出结果为15,15二进制为 1111
}
下面让我们看下如果赋值超出位域范围会出现什么情况:
int main(int argc, char *argv[])
{
Test b;
b.a=16;
printf("%d\n",b.a); //输出结果为0,16的二进制为 0001 0000
}
这里我们就可以看出其实在解析变量时实际仅解析的位域限制位数的值,也就是 0000前面的0001并没有被解析因此输出是0。那么我就可以猜测如果赋值为17输出应该为1。下面验证:
int main(int argc, char *argv[])
{
Test b;
b.a=17;
printf("%d\n",b.a); //输出结果为1,17二进制为 0001 0001
}
果然证实了我们的猜测。
下面我们在测试下如果赋值大于了类型取值范围但是没有大于位域的取值范围会出现什么情况:
typedef struct
{
unsigned char a:16;
unsigned short b:16;
}Test;
int main(int argc, char *argv[])
{
Test b;
b.a=257;
b.b=257;
printf("a = %d\n b = %d\n",b.a,b.b); //输出结果为 a = 1 b = 257
}
我们知道unsigned char 8个比特位取值范围为0到2^8-1也就是0-255。当我们输入一个大于255的数无论多大只取该数对应的最后8个比特位257二进制为 0001 0000 0001,unsigned char a:16; 位域定义的是16位,但是unsigned char 本身只有8位因此会按照8个比特位解析也就是解析的0000 0001,输出结果就是1。而unsigned short b:16;位域定义是16位,unsigned short占用内存也是16个比特位,所以b解析时候会按照16个比特位解析也就是 0000 0001 0000 0001;输出结果就是257。
sizeof查看占用内存
typedef struct
{
unsigned int a:64;
unsigned int b:16;
}TestA;
typedef struct
{
unsigned int a:1;
unsigned int b:1;
}TestB;
int main(int argc, char *argv[])
{
printf("TestA size = %d\nTestB size = %d\n",sizeof(TestA),sizeof(TestB)); //输出为TestA size = 16 TestB size = 4
}
通过上面可以看出TestB占用的就是一个int占用的4字节,TestA占用的是两个int占用的8字节,可以看出TestA中的变量a实际占用空间就是8字节,b占用2字节,结构体对齐原则必须是最大成员的整数倍也就是必须是8的整数倍所以TestA是4字节,TestB是16字节。结构体对齐规则不懂的可以查看我另一篇博客:
C语言结构体,共用体占用内存原理解析
总结
在结构体中使用位域限制结构体成员所占的内存空间,位域限制生效范围为 1到变量成员类型所占内存大小个比特位,小于1编译会出现错误,大于变量成员类型所占内存大小个比特位会失效相当于没有设置位域限制。