https://www.cnblogs.com/renyuan/archive/2013/05/26/3099766.htmlhtml
1.故事的起源
「endian」這個詞出自《格列佛遊記》。小人國的內戰就源於吃雞蛋時是究竟從大頭(Big-Endian)敲開仍是從小頭(Little-Endian)敲開,由此曾發生過六次叛亂,其中一個皇帝送了命,另外一個丟了王位。
咱們通常將endian翻譯成「字節序」,將big endian和little endian稱做「大尾」和「小尾」。
2.什麼是Big Endian和Little Endian?
在設計計算機系統的時候,有兩種處理內存中數據的方法。一種叫爲little-endian,存放在內存中最低位的數值是來自數據的最右邊部分(也就是數據的最低位部分)。 好比某些文件須要在不一樣平臺處理,或者經過Socket通訊。這方面咱們能夠藉助ntohl(), ntohs(), htonl(), and htons()函數進行格式轉換,
我的補充:一個操做數做htonl或ntohl結果不必定相同,當機器字節序跟網絡字節序恰好是僅僅big endian和little endian的區別時是相同的。
3. 如何理解Big Endian和Little Endian
舉個例子:
int a = 1;
a這個數自己的16進製表示是0x00 00 00 01
在內存中怎麼存儲呢?
若是你的CPU是intel x86架構的(基本上就是一般咱們說的奔騰cpu),那麼就是0x01 0x00 0x00 0x00 , 這也就是所謂的little-endian, 低字節存放在內存的低位.
若是你的CPU是老式AMD系列的(很老很老的那種,由於最新的AMD系列已是x86架構了), 它的字節序就是big-endian, 其內存存儲就是 0x00 0x00 0x00 0x01在內存中從高字節開始存放。
如今世界上絕大多數的CPU都是little-endian。
4. 瞭解big-endian和little-endian有什麼做用?
一個重要的做用就是了解在網絡上不一樣的機器間的數據如何傳輸。 假設咱們在網絡上有兩臺機器A和B, 其中A爲little-endian,B爲big-endian 機器A要傳輸上面的整數a給機器B,如何傳輸呢? 過程是這樣的:
機器A先把a在內存中的四個字節0x 01 0x00 0x00 0x00轉化爲網絡字節序0x00 0x00 0x00 0x01,而後一個字節一個字節(從0x00到0x01)喂到網絡上去 ,而後機器B從網絡上一個字節一個字節地取出四個字節0x00 0x00 0x00 0x01後又會轉化爲本地字節序 0x00 0x00 0x00 0x01後放入內存。於是B正確地獲得了來自A的數據a 。若是數據缺乏在網絡上的字節序轉換的話,狀況會怎樣呢?
機器A先把a由在內存的四個字節0x 01 0x00 0x00 0x00 一個字節一個字節地喂到網絡上,而後機器B從網絡上一個字節一個字節地收到0x 01 0x00 0x00 0x00並放入到內存中, B認爲他收到了0x01000000, 也就是十進制數1677216,這顯然是錯誤的.
5.如何判斷系統是Big Endian仍是Little Endian?
在/usr/include/中(包括子目錄)查找字符串BYTE_ORDER(或_BYTE_ORDER, __BYTE_ORDER),肯定其值。這個值通常在endian.h或machine/endian.h文件中能夠找到,有時在feature.h中,不一樣的操做系統可能有所不一樣。通常來講,Little Endian系統BYTE_ORDER(或_BYTE_ORDER,__BYTE_ORDER)爲1234,Big Endian系統爲4321。大部分用戶的操做系統(如windows, FreeBsd,Linux)是Little Endian的。少部分,如MAC OS ,是Big Endian 的。本質上說,Little Endian仍是Big Endian與操做系統和芯片類型都有關係。windows
在ARM體系中,每一個字單元包含4個字節單元或者兩個半字單元。在字單元中,4個字節哪個是高位字節,哪個是低位字節則有兩種不一樣的格式:big-endian和little-endian格式。在小端模式中,低位字節放在低地址,高位字節放在高地址;在大端模式中,低位字節放在高地址,高位字節放在低地址。網絡
若是將一個32位的整數0x12345678(如用UltraEdit打開某個文件看到的第一行頭四個字節是:"00000000h:12 34 56 78")存放到一個整型變量(int)中,這個整型變量(文件內容)採用大端或者小端模式在內存中的存儲由下表所示。架構
地址偏移函數 |
大端模式spa |
小端模式操作系統 |
0x00翻譯 |
12設計 |
78指針 |
0x01 |
34 |
56 |
0x02 |
56 |
34 |
0x03 |
78 |
12 |
對於文件內容 0x12345678,把前面("12")的看爲高端字節,後面("78")的看爲低端字節,那麼可使用"高高低低"(Little Endian),"高低高低"(Big Endian)的口訣。直觀的區分,若是發現內存的內容和文件的內容在順序上以4個字節顛倒,那麼他就是Little Edian。實現Big Endian和Little Endian主要是由編譯器指定的,一般是在CCFLAG 加參數,如: -DENDIAN_LITTLE,設定編譯爲小端字節。實際中用Trace 32能夠用Memory Dump查看內存內容,和寫入文件比較後判斷爲大端仍是小端。
若是將一個16位的整數0x1234存放到一個短整型變量(short)中。這個短整型變量在內存中的存儲在大小端模式由下表所示。
地址偏移 |
大端模式 |
小端模式 |
0x00 |
12 |
34 |
0x01 |
34 |
12 |
由上表所知,採用大小模式對數據進行存放的主要區別在於在存放的字節順序,大端方式將高位存放在低地址,小端方式將低位存放在低地址。
那麼該如何判斷CPU是大端模式仍是小端模式呢??
在C語言中,聯合體union的存放順序是全部成員都從低地址開始存放的。利用這一特色,能夠用聯合體變量判斷ARM或x86環境下,存儲系統是是大端仍是小端模式。
具體的代碼以下:
#include "stdio.h"
int main()
{
union w
{
int a; //4 bytes
char b; //1 byte
} c;
c.a=1;
if (c.b==1)
printf("It is Little_endian!/n");
else
printf("It is Big_endian!/n");
return 1;
}
數據在存放到內存裏的時候,有兩種存放方式,即:Big Endian 和 Little Endian,這兩個存取方式決定了內存存放數據的原則是 高高低低 原則 仍是 高低低高 原則。
高高低低--內存中的高位存放數據的高位,內存中的低位存放數據的低位(Little Endian )
高低低高--內存中的高位存放數據的低位,內存中的低位存放數據的高位(Big Endian )
好比:我有一個數據,是0xA5A1,它在存放到內存中是怎樣存放的呢?由於在咱們平時的書寫中,A5是高位,A1在低位,存放到內存中的時候,A1存放在0x4000這個位置,而A5存放在0x4001這個位置,高位存放在內存的高地址中,低位存放在低地址中,這種方式就是Little Endian 。
下面再粘一篇網友的總結的文章來詳細闡述一下這兩種方式的區別:
Big Endian 和 Little Endian 模式的區別(轉載)
談到字節序的問題,必然牽涉到兩大CPU派系。那就是Motorola的PowerPC系列CPU和Intel的x86系列CPU。PowerPC系列採用big endian方式存儲數據,而x86系列則採用little endian方式存儲數據。那麼究竟什麼是big endian,什麼又是little endian呢?
其實big endian是指低地址存放最高有效字節(MSB),而little endian則是低地址存放最低有效字節(LSB)。
用文字說明可能比較抽象,下面用圖像加以說明。好比數字0x12345678在兩種不一樣字節序CPU中的存儲順序以下所示:
Big Endian
低地址 高地址
----------------------------------------------------------------------------->
| 12 | 34 | 56 | 78 |
Little Endian
低地址 高地址
----------------------------------------------------------------------------->
| 78 | 56 | 34 | 12 |
從上面兩圖能夠看出,採用big endian方式存儲數據是符合咱們人類的思惟習慣的。
爲何要注意字節序的問題呢?你可能這麼問。固然,若是你寫的程序只在單機環境下面運行,而且不和別人的程序打交道,那麼你徹底能夠忽略字節序的存在。可是,若是你的程序要跟別人的程序產生交互呢?尤爲是當你把你在微機上運算的結果運用到計算機羣上去的話。
在這裏我想說說兩種語言。C/C++語言編寫的程序裏數據存儲順序是跟編譯平臺所在的CPU相關的,而JAVA編寫的程序則惟一採用big endian方式來存儲數據。試想,若是你用C/C++語言在x86平臺下編寫的程序跟別人的JAVA程序互通時會產生什麼結果?就拿上面的0x12345678來講,你的程序傳遞給別人的一個數據,將指向0x12345678的指針傳給了JAVA程序,因爲JAVA採起big endian方式存儲數據,很天然的它會將你的數據翻譯爲0x78563412。所以,在你的C程序傳給JAVA程序以前有必要進行字節序的轉換工做。
全部網絡協議也都是採用big endian的方式來傳輸數據的。因此有時咱們也會把big endian方式稱之爲網絡字節序。當兩臺採用不一樣字節序的主機通訊時,在發送數據以前都必須通過字節序的轉換成爲網絡字節序後再進行傳輸。ANSI C中提供了下面四個轉換字節序的宏。
一道C語言的試題:請寫一個C函數,若處理器是Big_endian的,則返回0;如果Little_endian的,則返回1。
解答:
int checkCPU()
{
{
union w
{
int a;
char b;
} c;
c.a = 1;
return (c.b == 1);
}
}
嵌入式系統開發者應該對Little-endian和Big-endian模式很是瞭解。採用Little-endian模式的CPU對操做數的存放方式是從低字節到高字節,而Big-endian模式對操做數的存放方式是從高字節到低字節。例如,16bit寬的數0x1234在Little- endian模式CPU內存中的存放方式(假設從地址0x4000開始存放)爲:
內存地址
存放內容
0x4000
0x34
0x400
0x12
而在Big-endian模式CPU內存中的存放方式則爲:
內存地址
存放內容
0x4000
0x12
0x4001
0x34
32bit寬的數0x12345678在Little-endian模式CPU內存中的存放方式(假設從地址0x4000開始存放)爲:
內存地址
存放內容
0x4000
0x78
0x4001
0x56
0x4002
0x34
0x4003
0x12
而在Big-endian模式CPU內存中的存放方式則爲:
內存地址
存放內容
0x4000
0x12
0x4001
0x34
0x4002
0x56
0x4003
0x78
聯合體union的存放順序是全部成員都從低地址開始存放,解答利用該特性,輕鬆地得到了CPU對內存採用Little-endian仍是Big-endian模式讀寫。