1. 程式人生 > 實用技巧 >位元組序問題之大小端模式講解

位元組序問題之大小端模式講解

一、什麼是大小端模式

大端模式(Big-Endian):指的是資料的低位儲存在記憶體的高地址中,而資料的高位儲存在記憶體的低地址中.

小端模式(Little-Endian):指的是資料的低位儲存在記憶體的低地址中,而資料的高位儲存在記憶體的高地址中。

上述的描述準確的說明了大小端模式的資料排列方式,但是還不夠直觀,下面我們來舉個例子:

如:數字 0x12345678 在記憶體中的表示形式為:

大端模式:

低地址 -----------------> 高地址
0x12 | 0x34 | 0x56 | 0x78

小端模式:

低地址 ------------------> 高地址
0x78 | 0x56 | 0x34 | 0x12

大小端模式的優劣勢比較

大端模式:強制轉換資料不需要調整位元組內容。

小端模式:符號位的判斷直接看第一個位元組,容易判斷正負。

基本上各自的優點就是對方的缺點,採用大端儲存模式儲存資料更符合人類的思維,採用小端儲存資料更方便計算機進行資料處理。

開發的時候必須要考慮大小端模式問題,若不考慮這些就收發資料會發生問題,因為儲存順序的不同意味著對接收資料的解析順序也不同。

二、為什麼有大小端模式

因為在計算系統中,我們是以位元組為單位的,每個地址單元都對應著一個位元組,一個位元組為8bit。對於位數大於8位的處理器,例如目前大部分都32位或者64位的處理器,由於暫存器寬度大於一個位元組,那麼必然存在著一個如果將多個位元組安排的問題。因此就導致了大端儲存模式和小端儲存模式。我們常用的X86結構是小端模式,而KEIL C51則為大端模式。很多的ARM,DSP都為小端模式。有些ARM處理器還可以由硬體來選擇是大端模式還是小端模式。

三、常見位元組序的模式

1、常見的CPU的位元組序

大端模式: PowerPC、IBM、Sun;

小端模式 : x86、DEC;

ARM既可以工作在大端模式,也可以工作在小端模式。

2、常見檔案的位元組序

大端模式:Adobe PS、JPEG、MacPaint;

小端模式:BMP、GIF、RTF;

另外,Java和所有的網路通訊協議都是使用Big-Endian的編碼。在對普通檔案進行處理也需要考慮端模式問題。在大端模式的處理器下對檔案的32,16位讀寫操作所得到的結果與小端模式的處理器不同。單純從軟體的角度理解上遠遠不能真正理解大小端模式的區別。事實上,真正的理解大小端模式的區別,必須要從系統的角度,從指令集,暫存器和資料匯流排上深入理解,大小端模式的區別。從實際應用的角度說,採用小端模式的處理器需要在軟體中處理端模式的轉換,因為採用小端模式的處理器在與小端外設互連時,不需要任何轉換。而採用大端模式的處理器需要在硬體設計時處理端模式的轉換。大端模式處理器需要在暫存器,指令集,資料匯流排及資料匯流排與小端外設的連線等等多個方面進行處理,以解決與小端外設連線時的端模式轉換問題。在暫存器和資料匯流排的位序定義上,基於大小端模式的處理器有所不同。

四、如何判斷CPU的位元組序

下面這段程式碼可以用來測試一下你的編譯器是大端模式還是小端模式:

short int x;
char x0,x1;
x=0x1122;
x0=((char*)&x)[0]; //低地址單元
x1=((char*)&x)[1]; //高地址單元
若x0=0x11,則是大端; 若x0=0x22,則是小端。從上面的程式還可以看出,資料定址時,用的是低位位元組的地址。

五、如何進行大小端轉換

這裡我們使用C語言大小端轉化呼叫庫函式:

在網路傳輸中,一般要求是大端,而intel處理器是小端,network to host理解為大端轉小端,而host to network 理解為小端轉大端,本質上大端小端的轉化演算法是一致的,沒有區別。

示例程式碼如下:

#include<stdio.h>
#include<Winsock2.h>
#pragma comment(lib,"ws2_32.lib")

int main(void) {
   unsigned int  ultest=0x12345678;
   unsigned short ustest=0x1234;

   printf("ultest小端:%x\n",ultest);
   printf("ustest小端:%x\n",ustest);
   ultest=htonl(ultest);/*unsigned int   大小端轉化*/
   ustest=htons(ustest);/*unsigned short 大小端轉化*/
   printf("ultest大端:%x\n",ultest);
   printf("ustest大端:%x\n",ustest);
   while(1);
}