计算机存储的大小端模式解析
----------------------------------------------------------------------------------------------------------------
先主要介紹了大小端問題的來源,作者應該是基于32位的機器來講解的。
在各種計算機體系結構中,對于字節、字等的存儲機制有所不同,因而引發了計算機 通信領 域中一個很重要的問題,即通信雙方交流的信息單元(比特、字節、字、雙字等等)應該以什么樣的順序進行傳送。如果不達成一致的規則,通信雙方將無法進行正 確的編/譯碼從而導致通信失敗。目前在各種體系的計算機中通常采用的字節存儲機制主要有兩種:Big-Endian和Little-Endian,下面先從字節序說起。
一、什么是字節序
字節序,顧名思義字節的順序,再多說兩句就是大于一個字節類型的數據在內存中的存放順序(一個字節的數據當然就無需談順序的問題了)。其實大部分人在實際的開發中都很少會直接和字節序打交道。唯有在跨平臺以及網絡程序中字節序才是一個應該被考慮的問題。
在所有的介紹字節序的文章中都會提到字節序分為兩類:Big-Endian和Little-Endian,引用標準的Big-Endian和Little-Endian的定義如下:
a) Little-Endian就是低位字節排放在內存的低地址端,高位字節排放在內存的高地址端。
b) Big-Endian就是高位字節排放在內存的低地址端,低位字節排放在內存的高地址端。(兩個明白一個就ok,大小端是相反的)
c) 網絡字節序:TCP/IP各層協議將字節序定義為Big-Endian,因此TCP/IP協議中使用的字節序通常稱之為網絡字節序。
1.1 什么是高/低地址端
首先我們要知道C程序映像中內存的空間布局情況:在《C專 家編程》中或者《Unix環境高級編程》中有關于內存空間布局情況的說明,大致如下圖:
----------------------- 最高內存地址 0xffffffff(32位2進制數,都是1的情況,16進制就是0xffffffff)
棧底
棧
棧頂
-----------------------(和我們通常意義上的畫圖是相反的,棧在內存里是向下增長,且發現內存自上而下,地址是由高到低變換,俗稱的高地址到低地址變化)
NULL (空洞)
-----------------------
堆
-----------------------
未初始化的數據
----------------------- 統稱數據段
初始化的數據
-----------------------
正文段(代碼段)
----------------------- 最低內存地址 0x00000000
由圖可以看出內存分布中,棧是向下增長的,而堆是向上增長的。以上圖為例如果我們在棧上分配一個unsigned char buf[4],那么這個數組變量在棧上是如何布局的呢?看下圖:
棧底 (高地址)
----------
buf[3]
buf[2]
buf[1]
buf[0]
----------
棧頂 (低地址)
其實,我們可以自己在編譯器里面創建一個數組,然后分別輸出數組種每個元素的地址,來驗證一下。
1.2 什么是高/低字節
現在我們弄清了高/低地址,接著考慮高/低字節。有些文章中稱低位字節為最低有效位,高位字節為最高有效位。如果我們有一個32位無符號整型0x12345678,那么高位是什么,低位又是什么呢? 其實很簡單。在十進制中我們都說靠左邊的是高位,靠右邊的是低位,在其他進制也是如此。就拿 0x12345678來說,從高位到低位的字節依次是0x12、0x34、0x56和0x78。
高/低地址端和高/低字節都弄清了。我們再來回顧 一下Big-Endian和Little-Endian的定義,并用圖示說明兩種字節序:
以unsigned int value = 0x12345678為例,分別看看在兩種字節序下其存儲情況,我們可以用unsigned char buf[4]來表示value:
Big-Endian: 低地址存放高位,如下圖:
棧底 (高地址)
---------------
buf[3] (0x78) -- 低位
buf[2] (0x56)
buf[1] (0x34)
buf[0] (0x12) -- 高位
---------------
棧頂 (低地址)
Little-Endian: 低地址存放低位,如下圖:
棧底 (高地址)
---------------
buf[3] (0x12) -- 高位
buf[2] (0x34)
buf[1] (0x56)
buf[0] (0x78) -- 低位
--------------
棧 頂 (低地址)
二、各種Endian
2.1 Big-Endian
計算機體系結構中一種描述多字節存儲順序的術語,在這種機制中最重要字節(MSB)存放在最低端的地址 上。采用這種機制的處理器有IBM3700系列、PDP-10、Mortolora微處理器系列和絕大多數的RISC處理器。MSB ? the most significant byte
+----------+
| 0x34 |<-- 0x00000021
+----------+
| 0x12 |<-- 0x00000020
+----------+
圖 1:雙字節數0x1234以Big-Endian的方式存在起始地址0x00000020中
在Big-Endian中,對于bit序列中的序號編排方式如下(以雙字節數0x8B8A=(1000 1011 1000 1010)2為例):
bit 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+-----------------------------------------+
val | 1 0 0 0 1 0 1 1 | 1 0 0 0 1 0 1 0 |
+----------------------------------------+
圖 2:Big-Endian的bit序列編碼方式
2.2 Little-Endian
計算機體系結構中 一種描述多字節存儲順序的術語,在這種機制中最不重要字節(LSB)存放在最低端的地址上。采用這種機制的處理器有PDP-11、VAX、Intel系列微處理器和一些網絡通信設備。該術語除了描述多字節存儲順序外還常常用來描述一個字節中各個比特的排放次序。
+----------+
| 0x12 |<-- 0x00000021
+----------+
| 0x34 |<-- 0x00000020
+----------+
圖3:雙字節數0x1234以Little-Endian的方式存在起始地址0x00000020中
在 Little-Endian中,對于bit序列中的序號編排和Big-Endian剛好相反,其方式如下(以雙字節數0x8B8A=(1000 1011 1000 1010)2為例):
bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+-----------------------------------------+
val | 1 0 0 0 1 0 1 1 | 1 0 0 0 1 0 1 0 |
+-----------------------------------------+
圖 4:Little-Endian的bit序列編碼方式
注意:通常我們說的主機序(Host Order)就是遵循Little-Endian規則。所以當兩臺主機之間要通過TCP/IP協議進行通信的時候就需要調用相應的函數進行主機序 (Little-Endian)和網絡序(Big-Endian)的轉換。
采用 Little-endian模式的CPU對操作數的存放方式是從低字節到高字節,而Big-endian模式對操作數的存放方式是從高字節到低字節。 32bit寬的數0x12345678在Little-endian模式CPU內存中的存放方式(假設從地址0x4000開始存放)為:
????????????????????????????????????????? 內存地址 ????0x4000??? ?0x4001 ?? ?0x4002 ?? ?0x4003
????????????????????????????????????????? 存放內容 ?? ?0x78?? ? ?? 0x56 ? ? ?? 0x34 ?? ? ?? 0x12
而在Big- endian模式CPU內存中的存放方式則為:
????????????????????????????????????????? 內存地址 ?? ?0x4000??? ?0x4001 ?? ?0x4002 ?? ?0x4003
????????????????????????????????????????? 存放內容 ?? ?0x12??? ???? 0x34 ?? ??? 0x56 ?? ? ?? 0x78
具體的區別如下:
?
?
三、Big-Endian和Little-Endian優缺點
Big-Endian優點:
首先提取高位字節,你總是可以由看看在偏移位置為0的字節來確定這個數字是正數還是負數(因為低地址存放高字節)。你不必知道這個數值有多長,或者你也不必過一些字節來看這個數值是否含有符號位。這個數值是以它們被打印出來的順序存放的,所以從二進制到十進制的函數特別有效。因而,對于不同要求的機器,在設計存取方式時就會不同。
Little-Endian優點:
提取一個,兩個,四個或者更長字節數據的匯編指令以與其他所有格式相同的方式進行:首先在偏移地址為0的地方提取最低位的字節,因為地址偏移和字節數是一對一的關系,多重精度的數學函數就相對地容易寫了。
如果你增加數字的值,你可能在左邊增加數字(高位非指數函數需要更多的數字)。 因此, 經常需要增加兩位數字并移動存儲器里所有Big-endian順序的數字,把所有數向右移,這會增加計算機的工作量。不過,使用Little- Endian的存儲器中不重要的字節可以存在它原來的位置,新的數可以存在它的右邊的高位地址里。這就意味著計算機中的某些計算可以變得更加簡單和快速。
四、請寫一個C函數,若處理器是Big_endian的,則返回0;若是Little_endian的,則返回1。(很經典的判斷處理器存儲模式的方法)
補充知識點:聯合union
能在同一個存儲空間里(不同時地)存儲不同類型數據的數據類型!是一個新的數據類型!典型應用是:表,使用聯合類型的數組……聯合和結構的建立方式一樣,一個聯合模版和一個聯合變量,可以一步到位的定義。也可以分步定義。
下面原作者也做了具體解釋
說明:
1 ?在c中,聯合體(共用體)的數據成員都是從低地址開始存放。
2 ?若是小端模式,由低地址到高地址,c.a存放為0x01 00 00 00,c.b被賦值為0x01;
? ————————————————————————————
? ?地址 0x00000000 0x00000001 0x00000002 0x00000003
? ?c.a ?01 ? ? ? ? 00 ? ? ? ? 00 ? ? ? ? 00
? ?c.b ?01 ? ? ? ? 00 ? ? ? ?
? ———————————————————————————— ?
3 ?若是大端模式,由低地址到高地址,c.a存放為0x00 00 00 01,c.b被賦值為0x0;
? ————————————————————————————
? ?地址 0x00000000 0x00000001 0x00000002 0x00000003
? ?c.a ?00 ? ? ? ? 00 ? ? ? ? 00 ? ? ? ? 01
? ?c.b ?00 ? ? ? ? 00 ? ? ? ? ? ? ? ??
? ———————————————————————————— ?
4 ?根據c.b的值的情況就可以判斷cpu的模式了。
舉例,一個16進制數是 0x11 22 33,其存放的位置是
地址0x3000 中存放11
地址0x3001 中存放22
地址0x3002 中存放33
連起來就寫成地址0x3000-0x3002中存放了數據0x112233
而這種存放和表示方式,正好符合大端。
另外一個比較好理解的寫法如下:
1 bool checkCPU()???? // 如果是大端模式,返回真 2 { 3 ?? ?short int test = 0x1234; 4 5 ?? ?if( *((char *)&test) == 0x12)???? // 低地址存放高字節數據 6 ?? ??? ?return true; 7 ?? ?else 8 ?? ??? ?return false; 9 } 10 11 int main(void) 12 { 13 ?? ?if( !checkCPU()) 14 ?? ??? ?cout<<"Little endian"<<endl; 15 ?? ?else 16 ?? ??? ?cout<<"Big endian"<<endl; 17 18 ?? ?return 0; 19 }
短整型test,32位機器里是2個字節大小存儲空間,0x1234,取地址,強轉為一字節的長度,截取的后8位,那么再次取值,和0x12比較。如果是大端模式,則剛好為0x12,下面原作者又給出了一個方案:其實大同小異。本質一樣。
或者下面兩種寫法也是可以的。
int main(void)
{
? ? short int a = 0x1234;
? ? char *p = (char *)&a;
? ? if( *p == 0x34)
? ? ? ? cout<<"Little endian"<<endl;
? ? else
? ? ? ? cout<<"Big endian"<<endl;
? ? return 0;
}
int main(void)
{
? ? short int a = 0x1234;
? ? char x0 , x1;
? ? x0 = ((char *)&a)[0];
? ? x1 = ((char *)&a)[1];
? ? if( x0 == 0x34)
? ? ? ? cout<<"Little endian"<<endl;
? ? else
? ? ? ? cout<<"Big endian"<<endl;
? ? return 0;
}
總結
以上是生活随笔為你收集整理的计算机存储的大小端模式解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C#的控件输入法变换
- 下一篇: ARM版本的IAR下载和安装