位域与大小端


大小端字节序介绍见:点击打开链接


大小端字节序指的是多字节类型的字节数据在内存中的存储顺序,字节内的各bit位置并不变化。

例如数据:0x12345678

在大端模式下的存储情况为:

地址0123
十六进制数据0x120x340x560x78
二进制数据0001 00100011 01000101 01100111 1000


在小端模式下的存储情况为:

地址0123
十六进制数据0x780x560x340x12
二进制数据0111 10000101 01100011 01000001 0010

由以上数据可见,大小端情况下只是字节数据的存储位置不同,字节内的各bit位置并不变话。


那什么情况下,大小端模式会影响bit位的存储顺序呢? 答:当结构体内的数据按位域定义时。这种情况下,结构体内按位域定义的数据类型,一定要区分大端字节序和小端字节序定义!


在对Struct(结构体)中成员进行内存分配的时候,“按排列顺序分配,先分配排在前面的”:

1.大端(big endian)模式,从高位向地位分配

对字节,先分配高字节(内存低地址中),再分配低字节(内存高地址中)。

对位域,先分配高bit位,再分配低bit位。

2.小端(little endian)模式,从地位向高位分配

对字节,先分配低字节(内存低地址中),再分配高字节(内存高地址中)。

对位域,先分配低bit位,再分配高bit位。



定义如下结构体:

typedef struct tagBigOrLittleData
{
	UINT32   uiA:5; /*对应数据赋值:00101*/
	UINT32   uiB:9; /*对应数据赋值:000001001*/
	UINT32   uiC:12;/*对应数据赋值:000000001100*/
	UINT32   uiD:6; /*对应数据赋值:000110*/

}BigOrLittleData_S;

定义结构体成员:stData 各个字段赋值如上面注释


大端模式下,stData 数据在内存中的值为:/*00101 000   001001 00  00000011  00 000110*/

地址0123
十六进制数据0x280x240x030x6
二进制数据00101 000
001001 0000000011
00 000110
解释uiA(5)+uiB(H3)uiB(L6)+uiC(H2)uiC(M8) uiC(L2)+uiD(6)


  结构体成员内存分配原则和上面描述一致:“按排列顺序分配,先分配排在前面的成员,在大端模式下,对位域,先对高bit位进行分配,在对低bit位进行分配”。



小端模式下,stData 数据在内存中的值为:/*001 00101   00 000001  00000011  000110 00*/

地址0123
十六进制数据0x250x010x030x18
二进制数据001  00101
00  000001
00000011
000110  00
解释uiB(L3)+uiA(5)uiC(L2)+uiB(H6)uiC(M8)uiD(6) + uiC(H2)

   结构体成员内存分配原则和上面描述一致:“按排列顺序分配,先分配排在前面的成员,在小端模式下,对位域,先对低bit位进行分配,在对高bit位进行分配”。


VS2010 验证代码及结果(小端模式):

typedef struct tagBigOrLittleData
{
	UINT32   uiA:5; /*对应数据赋值:00101*/
	UINT32   uiB:9; /*对应数据赋值:000001001*/
	UINT32   uiC:12;/*对应数据赋值:000000001100*/
	UINT32   uiD:6; /*对应数据赋值:000110*/

}BigOrLittleData_S;

int _tmain(int argc, _TCHAR* argv[])
{


	BigOrLittleData_S  stData = {0};

	stData.uiA = 5;
	stData.uiB = 9;
	stData.uiC = 12;
	stData.uiD = 6;

	/*001 00101   00 000001  00000011  000110 00*/

	getchar();

	return 0;
}





由此可见,同样一个按位域定义的结构体,大小端模式下,数据在内存中的存储顺序是完全不一样的,说白了,就是字节的值和位置都不一样。


所以,如果大端定义了上面的结构体,那么小端对应的结构体应该定义为:

typedef struct tagBigOrLittleData
{
    UINT32   uiD:6; /*对应数据赋值:000110*/
    UINT32   uiC:12;/*对应数据赋值:000000001100*/
    UINT32   uiB:9; /*对应数据赋值:000001001*/
    UINT32   uiA:5; /*对应数据赋值:00101*/

}BigOrLittleData_S;

此时,在小端模式下,stData 数据在内存中的值为:/*00 000110   00000011  001001 00  00101 000*/

地址0123
十六进制数据0x060x030x240x28
二进制数据00 00011000000011001001 0000101 000
解释uiC(L2)+uiD(6)uiC(M8)uiB(L6)+uiC(H2) uiA(5)+uiB(H3)

会发现,结构体各个位域字段“反序”定义以后,小端模式下,字节的值和大端模式的值相同了,但字节序还是反序的(0x06032428 对应 0x28240306),所以在使用时还要进行大小端字节序的转换!


从而,结构体中位域定义的成员,为了兼容大小端,存在以下规则:

1.结构体位域成员的定义,对每个成员的定义要区分大小端,对成员中位域的定义顺序进行翻转。

例如:

typedef struct tagBigOrLittleData
{
#ifdef BIG_ENDIAN
	UINT32   uiA:5; /*对应数据赋值:00101*/
	UINT32   uiB:9; /*对应数据赋值:000001001*/
	UINT32   uiC:12;/*对应数据赋值:000000001100*/
	UINT32   uiD:6; /*对应数据赋值:000110*/

	USHORT   usA:6;
	USHORT   usB:10
#else
	UINT32   uiD:6; /*对应数据赋值:000110*/
	UINT32   uiC:12;/*对应数据赋值:000000001100*/
	UINT32   uiB:9; /*对应数据赋值:000001001*/
	UINT32   uiA:5; /*对应数据赋值:00101*/

	USHORT   usB:10
	USHORT   usA:6;

#endif
}BigOrLittleData_S;


2.数据在不同主机之间传输使用时,要做字节序的转换。(发送时进行主机序到网络序的转换,接收时进行网络序到主机序的转换)

### 的大小及其在编程中的应用 是一种特殊的结构体成员,允许开发者指定其占用的数。这种机制在需要高效利用存储空间的场景中非常有用。例如,在嵌入式系统或底层开发中,程序可能需要硬件直接交互,而这些硬件寄存器通常只使用部分来表示特定的信息。 #### 的大小 在C/C++语言中,定义时可以通过冒号(:)后跟一个整数来指定该字段占用的数。例如: ```c struct Example { unsigned int flag1 : 1; // 占用1 unsigned int flag2 : 3; // 占用3 }; ``` 上述代码中,`flag1`和`flag2`分别占用了1和3的空间。这意味着整个结构体最多可以包含多个这样的字段,并且它们可以在同一个字节内共享空间[^2]。 #### 作用 - **节省内存**:通过精确控制每个字段所使用的比特数,可以在有限的内存资源下更有效地管理数据。 - **简化硬件操作**:许多硬件设备的数据寄存器由若干个标志组成,使用可以直接映射到这些寄存器,从而方便地进行读写操作。 - **提高效率**:对于某些特定的应用场景,如网络协议解析,能够减少不必要的数据转换步骤,提升处理速度。 #### 存储方式 的具体布局依赖于编译器实现以及目标平台的大端或小端模式。一般来说,同一体内的所有会尽可能紧凑地排列在一起,直到当前字节无法容纳下一个为止。此时,新的将被放置在下一个可用字节的起始置。需要注意的是,不同编译器对的支持可能存在差异,因此跨平台项目应特别注意这一点[^4]。 #### 程序设计中的应用 - **状态机表示**:可以用单个变量的不同来表示多种状态。 - **配置寄存器设置**:在驱动开发中,常常需要设置复杂的硬件寄存器值,使得这一过程更加直观易懂。 - **压缩数据格式**:当处理具有固定长度编码的数据流时,合理利用能够显著降低内存消耗。 ```c typedef struct { uint8_t enable : 1; // 启用/禁用标志 uint8_t mode : 2; // 操作模式选择 uint8_t value : 5; // 其他参数值 } ConfigRegister; ``` 以上示例展示了如何构建一个简单的配置寄存器模型,其中各部分分别对应不同的功能需求。
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值