位域的实际使用规则

转载 2018年04月17日 11:26:51

在某些特殊的情况下,对于内存的使用有着非常苛刻的要求(如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)来对齐,

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


位域妙用

所谓”位域“是把一个字节中的二进位划分为几 个不同的区域, 并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。           它实际上是C语言提供的一种数据结构。 使用位域...
  • CAIYAODENG
  • CAIYAODENG
  • 2015-11-05 19:04:13
  • 875

结构体之位域全面分析

结构体的定义   typedef struct TEST { . . . }Test,*pTest; struct TEST { . . . }test 这里的test是一个TES...
  • keep_moving_cqu
  • keep_moving_cqu
  • 2013-08-25 00:06:42
  • 2868

C语言中关于位域的介绍

有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可。 为了节省存储空间,并使处理简便,C语言又提供了一...
  • frank_jb
  • frank_jb
  • 2015-07-28 22:12:22
  • 4426

位域所占空间的大小

上一个项目中,遇到了一个问题:数据通过网络传输到不同的操作系统,反序列化时出现了segment fault。调试时发现原来是同一个结构体,在不同的操作系统(windows和linux)中,所占的空间大...
  • styshoo
  • styshoo
  • 2015-09-22 20:20:44
  • 603

寄存器位域、位操作等示例

#include /* 测试结果: struct test register_value: 0x0000fead enable: 1 type: 6 id: a value: fe union t...
  • subfate
  • subfate
  • 2014-06-07 11:02:31
  • 1169

结构体中的位域

位域  有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种...
  • birdzb
  • birdzb
  • 2007-10-12 14:46:00
  • 11963

理解C语言中的位域

C语言中的位域 有些信息在存储时,并不需要占用一个完整的字节,而只需要一个或几个二进制位即可;比如:在存放一个开关量时,只有0和1两种状态,只需要使用一个二进制位即可存储;为了节省存储空间,C语言提...
  • guoyong10721073
  • guoyong10721073
  • 2012-11-05 22:49:43
  • 2082

【C语言】位域的定义和使用

位域的定义和使用 C Bit Fields 转自http://www.360doc.com/content/09/0318/16/113975_2846158.shtml 位域的定...
  • xxxxxx91116
  • xxxxxx91116
  • 2013-07-02 13:45:00
  • 19100

结构体与位域的使用

C语言结构体对齐也是老生常谈的话题了。基本上是面试题的必考题。内容虽然很基础,但一不小心就会弄错。写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的...
  • yezi_zhi
  • yezi_zhi
  • 2016-09-01 11:10:44
  • 1487

史上最全的C位域总结

此篇文章是转载之史上最全的C位域总结经过个人查阅相关的资料和理解,现对C语言的位域做一个总结,对于位域的定义我在这里没有多说,我这里主要是对位域有歧义的地方进行系统的总结.我个人觉得总结的比较完整,如...
  • ccjjnn19890720
  • ccjjnn19890720
  • 2011-07-17 16:11:55
  • 1248
收藏助手
不良信息举报
您举报文章:位域的实际使用规则
举报原因:
原因补充:

(最多只允许输入30个字)