位域的实际使用规则

在某些特殊的情况下,对于内存的使用有着非常苛刻的要求(如L2),这个时候就可以用到位域这个东西,当然对于仅仅是单个结构体也是没有什么意义,因为节省的空间太少,如果某个结构体的维度非常的大,这个时候使用位域将会有非常显著的改变。

注:

注重可移植性的代码应该避免使用位域:由于下面这些与实现有关的依赖性,位域在不同的系统中有不同的结果。

1)int 位域被当作有符号数还是无符号数

2)位域中的最大数目。32 位 , 16位,64 位机

3)位域中的成员在内存中是从左向右分配,还是从右向左分配

4)当一个声明指定了两个位域,第二个位域比较大,无法容纳于第一个位域剩余的位时(如:32位),编译器有可能把第二个位域放在内存的下一个字,也有可能直接放在第一个位域的后面


1. 一个位域宽度应该不比它的类型宽度还要宽,如:

用signed 或是unsigned 显式的声明位域可以有效的1)中的因为编译器而导致结果不同的问题

位域的类型必须是int , signed int, unsigned int, 其他都不是标准的类型(既然要用位域,那就用unsigned int 好了,反正长度已经被写死了)。

其实这里的标准或者非常标准类型会对位域对齐有影响:位域的对齐宽度是sizeof(int)



2. 取地址操作符&不能应用在位域字段上;

ccs 可以通过view memory 来看具体的地址, 可以对于位域来说是非法的. 同理&也不能应用在位域字段上(cpu对于内在的访问都是以2的整倍数来的,而位域则有可能存在奇数的情况)


3. 位域字段不能是类的静态成员;

对于静态变量,你无法为它们指定其他存储类型,因为静态变量在程序运行之前创建,在程序的整个执行期间始终存在。



4. 位域字段在内存中的位置,和大小端有关

下面用的是小端:地址是从低到高,但是在0x000096AC开始的4个字节中,结构体xx的数据从上到下存到地址的低到高


下面用的是大端:地址是从低到高,但是在0x000095EC开始的4个字节中,结构体xx的数据从上到下存到地址的高到低


5. 位域的对齐(这里有一个非常重要的前提:位域的标准类型是int, signed int, unsigned int, 即对齐宽度是sizeof(int))

1. 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小:指的就是sizeof(int),则后面的字段将紧邻前一个字段存储,直到不能容纳为止;

上面的类型是unsigned char, sizeof(unsigned char) = 1, 根据这一条,的话b1 + b2 的位宽就已经大于8位了,根据下一2的原则,b2应该在0x000096ADbit 0-4, 不过从下图可以看出这种情况发生在b7的时候,因为b6 还是30个位宽 < 32 ,而b7已经不能容纳在32位中,所以从新的字节0x000096B0 开始算。所以说位域的标准类型是int , signed int,unsigned int, 即使你在使用的时候用的是其它的类型,但是在对齐的时候仍然是sizeof(int)对齐

2. 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;

上图的b7就反应了这一点

3.如果相邻的两个位域字段的类型不同,则各个编译器的具体实现有差异,VC6采取不压缩方式,GCC和Dev-C++都采用压缩方式;

根据下图可以知道,至少CCS上,都会强制的使用sizeof(int), 无视非标准的类型。

4. 整个结构体的总大小为最宽基本类型成员大小的整数倍。

从下图可以看出,即使是用了long long 类型,但还是用标准类型sizeof(int)来对齐

5. 如果位域字段之间穿插着非位域字段,则不进行压缩;(不针对所有的编译器)

这里的不进行压缩指的是对非位域字段,或者你可以更加简单的想成:

unsigned char c1  <== > unsigned char c1:8

unsigned int b6 <==> unsigned int b6:32

如何节省空间:

首先建议是都用unsigned int 类型,并且将要用位域的字段单独取出来做一个结构体,而又因为都是以标准的sizeof(int) 做对齐,

这个时候会满足第一个条件,所以如果位域越大,越放后面。当然这是简单的做法,其实根本的方法是凑sizeof(int)

而如果打乱这个顺序,就有可能变成了16个字节了

其实规则就是凑sizeof(int),只是说从小到大排相对的字节占用还是比较小的,当然自己 算一下也是很简单的

而类型可以根据位宽之和来定义如:(根据4. 整个结构体的总大小为最宽基本类型成员大小的整数倍。)

位宽之和小于8,而且都是1的话就用unsigned char, 如果粒度有大于8 小于16的,那么用unsigned short,

如果大于16 小32就用unsigned int, 只是说在不是同一个类型的时候,对齐是以标准类型sizeof(int)来对齐,

所以为了代码能通俗易懂和兼容性,应该定义成同一个类型


阅读更多
个人分类: 嵌入式基础
上一篇通信大小端字节序相关问题总结
下一篇数字签名原理简介(附数字证书)
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭