位域的实际使用规则

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

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


  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
联合体(Union)和位(Bit-field)是C语言中的特性,它们可以单独使用,也可以结合使用来优化内存使用和提高程序性能。 1. 联合体(Union): - 联合体是一种特殊的数据类型,它允许在同一块内存空间中存储不同类型的数据。 - 联合体的所有成员共享同一块内存空间,不同成员变量的值会互相覆盖。 - 联合体的大小等于最大成员的大小,以容纳最大的数据类型。 ```c union Data { int i; float f; char str[20]; }; int main() { union Data data; data.i = 10; printf("data.i: %d\n", data.i); data.f = 3.14; printf("data.f: %f\n", data.f); strcpy(data.str, "Hello"); printf("data.str: %s\n", data.str); return 0; } ``` 在上面的示例中,我们定义了一个联合体 `Data`,它包含了整型 `i`、浮点型 `f` 和字符数组 `str` 作为成员变量。这些成员变量共享同一块内存空间,根据不同的赋值会有不同的输出结果。 2. 位(Bit-field): - 位是一种用于在一个字节或更小的空间中存储多个标志位或数据的机制。 - 位使用冒号`:`来声明成员变量所占用的位数。 - 位成员变量的类型必须是整型(signed或unsigned)或枚举类型。 ```c struct Flags { unsigned int flag1: 1; // 1位 unsigned int flag2: 1; // 1位 unsigned int flag3: 1; // 1位 }; int main() { struct Flags flags; flags.flag1 = 1; flags.flag2 = 0; flags.flag3 = 1; printf("flags.flag1: %d\n", flags.flag1); printf("flags.flag2: %d\n", flags.flag2); printf("flags.flag3: %d\n", flags.flag3); return 0; } ``` 在上面的示例中,我们定义了一个结构体 `Flags`,其中包含了三个位成员变量 `flag1`、`flag2` 和 `flag3`,每个成员变量都占用一个位。通过设置不同的值,可以表示不同的标志位。 3. 联合体和位的结合使用: - 联合体和位可以结合使用来优化内存使用和提高程序性能。 - 在联合体中定义一个包含位成员的结构体,通过联合体来访问和操作这些位成员。 - 这样可以在同一块内存中存储和管理不同类型的数据,节省内存空间,并且可以使用来表示标志位或状态信息。 ```c union Flags { unsigned char value; struct { unsigned char flag1: 1; // 1位 unsigned char flag2: 1; // 1位 unsigned char flag3: 1; // 1位 unsigned char reserved: 5; // 5位,保留位 } flags; }; int main() { union Flags flags; flags.value = 0x00; flags.flags.flag1 = 1; flags.flags.flag2 = 0; flags.flags.flag3 = 1; printf("flags.value: 0x%02X\n", flags.value); return 0; } ``` 在上面的示例中,我们定义了一个联合体 `Flags`,其中包含了一个无符号字符成员 `value` 和一个包含位成员的结构体 `flags`。通过设置位成员的值,可以在一个字节的空间中存储多个标志位。 需要注意的事项: - 确保位成员的位数不超过其所属数据类型的位数范围,否则可能导致未定义的行为。 - 跨平台兼容性:位的存储方式依赖于底层硬件平台的字节序和对齐规则,因此在跨平台开发中要谨慎使用。 - 可读性和可维护性:位使用可能会增加代码的复杂性,降低代码的可读性和可维护性。需要权衡使用带来的好处和代码清晰度之间的关系。 结合使用联合体和位可以在某些场景下提供更灵活和高效的内存管理方式,但需要根据实际需求和具体情况来决定是否使用,并注意上述事项。 如果还有其他问题,请随时提问!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值