大小端模式

大小端模式

大端模式,是指數據的高位元組保存在記憶體的低地址中,而數據的低位元組保存在記憶體的高地址中,這樣的存儲模式有點兒類似於把數據當作字元串順序處理:地址由小向大增加,而數據從高位往低位放;這和我們的閱讀習慣一致。

小端模式,是指數據的高位元組保存在記憶體的高地址中,而數據的低位元組保存在記憶體的低地址中,這種存儲模式將地址的高低和數據位權有效地結合起來,高地址部分權值高,低地址部分權值低。

基本介紹

  • 中文名:大小端模式
  • 外文名: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) -- 低位
--------------
低地址
記憶體地址
小端模式存放內容
大端模式存放內容
0x4000
0x78
0x12
0x4001
0x56
0x34
0x4002
0x34
0x56
0x4003
0x12
0x78

名詞解析

大端模式
所謂的大端模式(Big-endian),是指數據的高位元組,保存在記憶體的低地址中,而數據的低位元組,保存在記憶體的高地址中,這樣的存儲模式有點兒類似於把數據當作字元串順序處理:地址由小向大增加,而數據從高位往低位放;
例子:
0000430: e684 6c4e 0100 1800 53ef 0100 0100 0000
0000440: b484 6c4e 004e ed00 0000 0000 0100 0000
在大端模式下,前32位應該這樣讀: e6 84 6c 4e ( 假設int占4個位元組)
記憶方法: 地址的增長順序與值的增長順序相反
小端模式
所謂的小端模式(Little-endian),是指數據的高位元組保存在記憶體的高地址中,而數據的低位元組保存在記憶體的低地址中,這種存儲模式將地址的高低和數據位權有效地結合起來,高地址部分權值高,低地址部分權值低,和我們的邏輯方法一致。
例子:
0000430: e684 6c4e 0100 1800 53ef 0100 0100 0000
0000440: b484 6c4e 004e ed00 0000 0000 0100 0000
在小端模式下,前32位應該這樣讀: 4e 6c 84 e6( 假設int占4個位元組)
記憶方法: 地址的增長順序與值的增長順序相同
大小端模式
為什麼會有大小端模式之分呢?這是因為在計算機系統中,我們是以位元組為單位的,每個地址單元都對應著一個位元組,一個位元組為 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處理器還可以隨時在程式中(在ARM Cortex 系列使用REV、REV16、REVSH指令)進行大小端的切換。
圖解
對於0x11223344 儲存如下
大小端模式
名字由來
喬納森·斯威夫特的著名諷刺小說《格列夫遊記》中,小人國內部分裂成Big-endian和Little-endian兩派,區別在於一派要求從雞蛋的大頭把雞蛋打破,另一派要求從雞蛋的小頭把雞蛋打破。斯威夫特藉以諷刺英國的政黨之爭,在計算機工業中指數據儲存順序的分歧。
編輯器模式
下面這段代碼可以用來測試一下你的編譯器是大端模式還是小端模式:
short int x;char x0,x1;x=0x1122;x0=((char*)&x)[0]; //低地址單元x1=((char*)&x)[1]; //高地址單元
若x0=0x11,則是大端; 若x0=0x22,則是小端......
從上面的程式還可以看出,數據定址時,用的是低位位元組的地址。
簡單大小端轉換的宏
#definesw16(x)\ ((short)(\ (((short)(x)&(short)0x00ffU)<<8)|\ (((short)(x)&(short)0xff00U)>>8)))

作業系統

static union { char c[4]; unsigned long l; }endian_test = { { 'l', '?', '?', 'b' } };#define ENDIANNESS ((char)endian_test.l)
(如果ENDIANNESS=’l’表示系統為little endian,為’b’表示big endian )。

判斷處理器

通過下列的程式可以確認在某個硬體平台上的某個作業系統是大端還是小端:
VB6:
' 注意這個CopyMemory的聲明與一般的不一樣,' 一般的都是(pDst As Any, pSrc As Any, ByVal ByteLen As Long)' 為了能夠逐位元組訪問u,所以前面兩個參數改成了按值傳遞,配合VarPtr函式獲取變數地址Private Declare Sub CopyMemory Lib "kernel32" Alias _    "RtlMoveMemory" (ByVal pDst As Long, ByVal pSrc As Long, ByVal ByteLen As Long)Private Sub Form_Load()    ' VB的Integer占用2個位元組!Long才是4個位元組    ' 32位應用程式的指針是4個位元組    Dim uptr As Long    Dim aptr As Long    Dim bptr As Long    Dim cptr As Long    Dim dptr As Long        Dim u As Long        ' 儲存u從低地址到高地址的4個位元組    Dim a As Byte    Dim b As Byte    Dim c As Byte    Dim d As Byte        u = 367328153 ' 十六進制數:15 E4 FB 99        uptr = VarPtr(u) ' VarPtr函式是內置函式,但是msdn不說,作用是獲取變數的地址        aptr = VarPtr(a)    bptr = VarPtr(b)    cptr = VarPtr(c)    dptr = VarPtr(d)        CopyMemory aptr, uptr + 0, 1 ' 將u逐位元組按順序寫入a,b,c,d    CopyMemory bptr, uptr + 1, 1    CopyMemory cptr, uptr + 2, 1    CopyMemory dptr, uptr + 3, 1        ' Windows系統,英特爾處理器:最後輸出的是99 FB E4 15    MsgBox Hex(a) & " " & Hex(b) & " " & Hex(c) & " " & Hex(d)End Sub
在英特爾處理器,Windows10作業系統上,對話框顯示的結果是99 FB E4 15,與直接求出來的16進制值15 E4 FB 99正好相反,所以是小端的。
C++語言(VS2013下,控制台項目):
#include <iostream>using namespace std;typedef unsigned char byte;// 轉換char(視為整數類型)為16進制字元串void ChtoHex(byte Val, char* dest){    // 輾轉相除法,倒序看得到結果    byte tmp = Val % 16;    if (tmp >= 0 && tmp <= 9)    {        dest[1] = '0' + tmp;    }    else if (tmp >= 10 && tmp <= 15)    {        dest[1] = 'A' + tmp - 10;    }    tmp = (Val/16) % 16;    if (tmp >= 0 && tmp <= 9)    {        dest[0] = '0' + tmp;    }    else if (tmp >= 10 && tmp <= 15)    {        dest[0] = 'A' + tmp - 10;    }    // 設定'\0'    dest[2] = '\0';}// 主函式int main(){    int u = 367328153; // 原始數據,8位16進制為15 E4 FB 99    byte a, b, c, d;   // u從低地址到高地址的四個位元組    // a~d對應的16進制字元串,預留3個字元    char Sa[3], Sb[3], Sc[3], Sd[3];     byte* k = (byte*)&u;    a = k[0];    b = k[1];    c = k[2];    d = k[3];    // 轉成16進制字元串    ChtoHex(a, Sa);    ChtoHex(b, Sb);    ChtoHex(c, Sc);    ChtoHex(d, Sd);    cout << Sa << " " << Sb << " " << Sc << " " << Sd << endl;    system("pause");    return 0;}
在英特爾處理器,Windows10作業系統上,控制台顯示的結果是99 FB E4 15,與直接求出來的16進制值15 E4 FB 99正好相反,所以也證明是小端的。
C語言(VC++6.0,控制台工程):
#include <stdio.h>typedef unsigned char byte;// 轉換char(視為整數類型)為16進制字元串void ChtoHex(byte Val, char* dest){    // 輾轉相除法,倒序看得到結果    byte tmp = Val % 16;    if (tmp >= 0 && tmp <= 9)    {        dest[1] = '0' + tmp;    }    else if (tmp >= 10 && tmp <= 15)    {        dest[1] = 'A' + tmp - 10;    }    tmp = (Val/16) % 16;    if (tmp >= 0 && tmp <= 9)    {        dest[0] = '0' + tmp;    }    else if (tmp >= 10 && tmp <= 15)    {        dest[0] = 'A' + tmp - 10;    }    // 設定\0    dest[2] = '\0';}// 主函式void main(){    int u = 367328153; // 原始數據,8位16進制為15 E4 FB 99    byte a, b, c, d;   // u從低地址到高地址的四個位元組    // a~d對應的16進制字元串,預留3個字元    char Sa[3], Sb[3], Sc[3], Sd[3];     byte* k = (byte*)&u;    a = k[0];    b = k[1];    c = k[2];    d = k[3];    // 轉成16進制字元串    ChtoHex(a, Sa);    ChtoHex(b, Sb);    ChtoHex(c, Sc);    ChtoHex(d, Sd);    printf("%s %s %s %s\n", Sa, Sb, Sc, Sd);    scanf_s("%d", &a);}
在英特爾處理器,Windows10作業系統上,結果跟上面是一樣的,輸出的16進制數是反序的,證明是小端系統。
C#(VS2013下,控制台項目)
using System;namespace ConsoleApplication1{    class Program    {        static void Main(string[] args)        {           int u = 367328153;  // 原始數據,8位16進制為15 E4 FB 99           byte[] bytes;       // u從低地址到高地址的四個位元組            // 獲取            bytes = System.BitConverter.GetBytes(u);            Console.WriteLine(                bytes[0].ToString("X") + " " +                bytes[1].ToString("X") + " " +                bytes[2].ToString("X") + " " +                bytes[3].ToString("X")                );            Console.ReadLine();        }    }}
在英特爾處理器,Windows10作業系統上,結果跟上面還還是一樣的,輸出的16進制數是反序的,證明是小端系統。
MDK(Keil5,STM32F407)C語言:
#include "stm32f4xx.h"int main(void){     int u = 367328153; // 原始數據15 E4 FB 99    int* k = &u;    return 0;}
編譯連線然後下載到開發板上,然後啟動調試,通過監視視窗可以看到u的地址,然後在記憶體視窗可以看到位元組序是反序的,所以說明STM32F407是小端的。據某些資料說ARM核心是可以設定大小端的,但是STM32是外設自動進入了小端,似乎是無法調整的。
大小端模式
89C52(Keil5)C語言
最後來一個大端的例子。手頭上沒有51的開發板,所以用的是軟體仿真。
#include <reg52.h>int main(){    int longbit = sizeof(long);        long u = 367328153; // 原始數據15 E4 FB 99    long* k = &u;        return 0;}
注意看了,C52是8位的處理器,long才是4個位元組的,看監視視窗longbit的值就知道了(紫色框)。然後再看記憶體視窗,就會發現u的存儲是跟原始數據給的順序是一樣的,所以C51和C52是大端的!!
大小端模式

現階段狀況

目前Intel的80x86系列晶片是唯一還在堅持使用小端的晶片,ARM晶片默認採用小端,但可以切換為大端;而MIPS等晶片要么採用全部大端的方式儲存,要么提供選項支持大端——可以在大小端之間切換。另外,對於大小端的處理也和編譯器的實現有關,在C語言中,默認是小端(但在一些對於單片機的實現中卻是基於大端,比如Keil 51C),Java是平台無關的,默認是大端。在網路上傳輸數據普遍採用的都是大端。

相關詞條

熱門詞條

聯絡我們