大小端字节序介绍见:点击打开链接
大小端字节序指的是多字节类型的字节数据在内存中的存储顺序,字节内的各bit位置并不变化。
例如数据:0x12345678
在大端模式下的存储情况为:
地址 0 1 2 3 十六进制数据 0x12 0x34 0x56 0x78 二进制数据 0001 0010 0011 0100 0101 0110 0111 1000
在小端模式下的存储情况为:
地址 0 1 2 3 十六进制数据 0x78 0x56 0x34 0x12 二进制数据 0111 1000 0101 0110 0011 0100 0001 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 数据在内存中的值为:/*00101 000 001001 00 00000011 00 000110*/
地址 | 0 | 1 | 2 | 3 |
十六进制数据 | 0x28 | 0x24 | 0x03 | 0x6 |
二进制数据 | 00101 000 |
001001 00 | 00000011 |
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*/
地址 | 0 | 1 | 2 | 3 |
十六进制数据 | 0x25 | 0x01 | 0x03 | 0x18 |
二进制数据 | 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;
地址 | 0 | 1 | 2 | 3 |
十六进制数据 | 0x06 | 0x03 | 0x24 | 0x28 |
二进制数据 | 00 000110 | 00000011 | 001001 00 | 00101 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;