位域與大小端


大小端字節序介紹見:點擊打開鏈接


大小端字節序指的是多字節類型的字節數據在內存中的存儲順序,字節內的各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 各個字段賦值如上面註釋


大端模式下,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;

此時,在小端模式下,stData 數據在內存中的值爲:/*00 000110   00000011  001001 00  00101 000*/

地址 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;


2.數據在不同主機之間傳輸使用時,要做字節序的轉換。(發送時進行主機序到網絡序的轉換,接收時進行網絡序到主機序的轉換)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章