??這是因為在計算機系統中,我們是以字節為單位的,每個地址單元都對應著一個字節,一個字節為8bit。但是在C語言中除了8bit的char之外,還有16bit的short型,32bit的long型(要看具體的編譯器),另外,對于位數大于8位的處理器,例如16位或者32位的處理器,由于寄存器寬度大于一個字節,那么必然存在著一個如何將多個字節安排
的問題。因此就導致了大端存儲模式
和小端存儲模式
。
??例如一個16bit的short型x,在內存中的地址為0x0010,x的值為0x1122,那么0x11為高字節,0x22為低字節。對于大端模式,就將0x11放在低地址中,即0x0010中,0x22放在高地址中,即0x0011中。小端模式,剛好相反。我們常用的X86結構是小端模式,而KEIL C51則為大端模式。很多的ARM,DSP都為小端模式。有些ARM處理器還可以由硬件來選擇是大端模式還是小端模式。
那什么是大端和小端呢?
? Little-Endian:低位字節排放在內存的低地址端,高位字節排放在內存的高地址端。示例數字0x12 34 56 78在內存中的表示形式:
??內存 低地址 -----------------> 高地址
??0x78 | 0x56 | 0x34 | 0x12 *
??低位子節 -----------------> 高位子節*
? Big-Endian:高位字節排放在內存的低地址端,低位字節排放在內存的高地址端。示例數字0x12 34 56 78在內存中的表示形式:
??內存 低地址 -----------------> 高地址
???? 0x12 | 0x34 | 0x56 | 0x78
??高位子節 -----------------> 低位子節
可見,大端模式和字符串的存儲模式類似。但是也有各自的特點:
? 小端模式 :強制轉換數據不需要調整字節內容,1、2、4字節的存儲方式一樣。
? 大端模式 :符號位的判定固定為第一個字節,容易判斷正負。
則可以通過以下方式判斷機器的子節序
BOOL IsBigEndian()
{
int a = 0x1234;//如果要存在至數組中,大端應為 arr[0] =a&0xff00 > 16= 0x12,arr[1]=a&0xff=0x34;
char b = *(char *)&a; // 通過將int強制類型轉換成char單字節,通過判斷起始存儲位置。即等于 取b等于a的低地址部分;
if( b == 0x12)// 如果是大端模式,則a的低地址內存存放的應該是高位子節0x12
{
return TRUE;
}
return FALSE;
}
或者 利用聯合體union成員的存放順序都是從低地址開始的特性來做判斷。
BOOL IsBigEndian()
{
union NUM
{
int a;
char b;
}num;
num.a = 0x1234;
if( num.b == 0x12 )
{
return TRUE;
}
return FALSE;
}
大小端轉換示例
//16位字節:
#define BigtoLittle16(A) (( ((uint16)(A) & 0xff00) >> 8) | \
(( (uint16)(A) & 0x00ff) << 8))
//32位子節:
#define BigtoLittle32(A) ((( (uint32)(A) & 0xff000000) >> 24) | \
(( (uint32)(A) & 0x00ff0000) >> 8) | \
(( (uint32)(A) & 0x0000ff00) << 8) | \
#define ntohs(n) //16位數據類型網絡字節順序到主機字節順序的轉換
#define htons(n) //16位數據類型主機字節順序到網絡字節順序的轉換
#define ntohl(n) //32位數據類型網絡字節順序到主機字節順序的轉換
#define htonl(n) //32位數據類型主機字節順序到網絡字節順序的轉換
借用一個 實際中的例子
雖然很多時候,字節序的工作已由編譯器完成了,但是在一些小的細節上,仍然需要去仔細揣摩考慮,尤其是在以太網通訊、MODBUS通訊、軟件移植性方面。這里,舉一個MODBUS通訊的例子。在MODBUS中,數據需要組織成數據報文,該報文中的數據都是大端模式,即低地址存高位,高地址存低位。假設有一16位緩沖區m_RegMW[256],因為是在x86平臺上,所以內存中的數據為小端模式:m_RegMW[0].low、m_RegMW[0].high、m_RegMW[1].low、m_RegMW[1].high……
為了方便討論,假設m_RegMW[0] = 0x3456; 在內存中為0x56、0x34。
現要將該數據發出,如果不進行數據轉換直接發送,此時發送的數據為0x56,0x34。而Modbus是大端的,會將該數據解釋為0x5634而非原數據0x3456,此時就會發生災難性的錯誤。所以,在此之前,需要將小端數據轉換成大端的,即進行高字節和低字節的交換,此時可以調用步驟五中的函數BigtoLittle16(m_RegMW[0]),之后再進行發送才可以得到正確的數據。