大端和小端

  在計算機中是以字節爲單位,每一個地址對應一個字節,一個字節8bit。在C中,除了8bit的char之外,還有16bit的short,32位的int,64位long,固然具體要由編譯器決定,能夠經過sizeof來獲取不一樣類型在內存中佔用的字節數。在計算機系統中,當物理單位的長度大於1個字節時,就要區分字節順序。常見的字節順序有兩種:Big Endian(High-byte first)和Litter Endian(Low-byte first),固然還有其餘字節順序,但不常見,例如Middle Endian。html

1、最高有效位、最低有效位c#

  要理解Big Endian和Little Endian,首先要搞清楚MSB和LSB。windows

  一、MSB(Most Significant Bit)最高有效位瀏覽器

    在一個n位二進制數字中n-1位,也就是最左邊的位。網絡

  二、LSB(Least Significant Bit)最低有效位架構

    指最右邊的位。函數

  例如:一個int類型的整型123456789ui

    二進制表達方式:0000 0111 0101 1011 1100 1101 0001 0101(從右向左,每4bit對齊,最左邊(高位)不夠用0補齊)this

    十六進制表達方式:0 7 5 B C D 1 5編碼

    按照上述關於MSB和LSB的意思,在二進制表達方式中,bit從0開始,從右向左,bit0爲最低有效位,而bit23爲最高有效位。而咱們通常稱左邊的0x07爲高位字節,0x15爲低位字節。

    再通俗一點解釋就是:8421碼的,8這端爲高位,1這端爲低位,相應的字節則分別稱爲高位字節和低位字節。

2、內存地址

  在內存中,多字節對象都是被存儲爲連續的字節序列。例如在C語言中,一個類型爲int的變量n,若是其存儲的首個字節的地址爲0x1000,那麼剩餘3個字節的地址將存儲在0x1001~0x1003。總之,無論具體字節順序是以什麼方式排列,內存地址的分配通常是從小到大的增加。咱們常把0x1000稱爲低地址端,把0x1003稱爲高地址端。

3、大端和小端

  搞清楚MSB、LSB、高位字節、低位字節、內存地址以後,再理解大端和小端,就至關容易了,先看看概念:

    小端Little Endian:低字節存放在低地址,低位字節排放在內存的低地址端,高位字節排放在內存的高地址端。

    大端Big Endian:高字節存放在低地址,即高位字節排放在內存的低地址端,低位字節排放在內存的高地址端。

  以二節中的例子int類型整數123456789爲例:

    小端在內存中排列:0x15 0xCD 0x5B 0x07 (低位在前)

    大端在內存中排列:0x07 0x5B 0xCD 0x15 (高位在前)

  從例子中能夠看出小端比較符合人的思惟,而大端則看上去很是直觀。

  注:

    一、例子中是假設編譯器支持int爲32位的前提下,若是是16位,那大端的排列則爲:0xCD 0x15 0x07 0x5B。

    二、大小端通常是由CPU架構決定,常見的Intel、AMD的CPU使用的是小端字節序,而PowerPC使用的是大端字節序,有些ARM處理器還能夠選擇用大端仍是小端模式,具體請自行查閱。

    三、c#中,字節序跟編譯平臺所在的CPU相關,例如在Intel x86 CPU架構的windows平臺中,c#採用的小端序。而Java因爲其JVM屏蔽了不一樣CPU架構致使的字節序差別,因此默認採用大端字節序。因此,大小端模式是由CPU決定,而編譯器又可能會改變這種模式。

字節序 內存地址 int(16bit) int(32bit) 特色
小端 0x1001,0x1002,0x1003,0x1004 0x15 0xCD 0x5B 0x07 0x15 0xCD 0x5B 0x07 低地址端存儲低位字節,低位在前
大端 0x1001,0x1002,0x1003,0x1004 0xCD 0x15 0x07 0x5B 0x07 0x5B 0xCD 0x15 低地址端存儲高位字節,高位在前

4、網絡字節序和主機字節序

  網絡字節序(Network Order):TCP/IP各層協議將字節序定義爲Big Endian,所以TCP/IP協議中使用的字節序一般稱之爲網絡字節序。

  主機字節序(Host Order):整數在內存中保存的順序,它遵循Little Endian規則(不必定,要看主機的CPU架構)。因此當兩臺主機之間要經過TCP/IP協議進行通訊的時候就須要調用相應的函數進行主機序列(Little Endian)和網絡序(Big Endian)的轉換。

  若是是作跨平臺開發時,雙方須要協商好字節序,而後根據程序運行的環境,肯定是否須要字節序轉換。

例如約定的通信字節序位Big Endian,默認的windows採用的Little Endian,那收到數據後就須要作轉換操做。

5、C#位操做符

  這裏簡單記錄一下C#的位操做符,方便之後本身查閱,也方便理解後面的講解。

  一、按位與&

    1&0爲0;0&0爲0;1&1爲1。

  二、按位或|

    1|0爲1;0|0爲0;1|1爲1。

  三、按位取反~

    ~1爲0;~0爲1。

  四、按位異或^

    1^1爲0;0^0爲0;1^0爲1。相等得0,相異等1。

  五、左移<<

    位左移運算,將整個數向左移若干位,左移後空出的部分用0補齊。

  六、右移>>

    位右移運算,將整個數向右移若干位,右移後空出的部分用0補齊。

6、C#中關於大端和小端的轉換
  一、重複輪子

using System;

namespace Framework.NetPackage.Common
{
    /// <summary>
    /// 字節序轉換輔助類
    /// </summary>
    public static class Endian
    {
        public static short SwapInt16(this short n)
        {
            return (short)(((n & 0xff) << 8) | ((n >> 8) & 0xff));
        }

        public static ushort SwapUInt16(this ushort n)
        {
            return (ushort)(((n & 0xff) << 8) | ((n >> 8) & 0xff));
        }

        public static int SwapInt32(this int n)
        {
            return (int)(((SwapInt16((short)n) & 0xffff) << 0x10) |
                          (SwapInt16((short)(n >> 0x10)) & 0xffff));
        }

        public static uint SwapUInt32(this uint n)
        {
            return (uint)(((SwapUInt16((ushort)n) & 0xffff) << 0x10) |
                           (SwapUInt16((ushort)(n >> 0x10)) & 0xffff));
        }

        public static long SwapInt64(this long n)
        {
            return (long)(((SwapInt32((int)n) & 0xffffffffL) << 0x20) |
                           (SwapInt32((int)(n >> 0x20)) & 0xffffffffL));
        }

        public static ulong SwapUInt64(this ulong n)
        {
            return (ulong)(((SwapUInt32((uint)n) & 0xffffffffL) << 0x20) |
                            (SwapUInt32((uint)(n >> 0x20)) & 0xffffffffL));
        }
    }
}

  二、BCL庫支持的函數

    System.Net.IPAddress.HostToNetworkOrder、System.Net.IPAddress.NetworkToHostOrder,這兩個函數的內部實現和上面重複輪子原理如出一轍。

7、關於負數

  在計算機中,負數以其絕對值的補碼形式表示,不明白能夠查閱九中貼出的相關資源。關於負數的字節序跟通常整數的字節序處理沒有任何區別。

8、關於漢字編碼以及與字節序的關係

  一、對於gb23十二、gbk、gb18030、big5,其編碼某個漢字產生的字節順序,由其編碼方案自己決定,不受CPU字節序的影響。其實這幾種編碼的字節序和大端模式的順序是一致的。

  在使用GB2312的程序一般採用EUC儲存方法,以便兼容於ASCII。瀏覽器編碼表上的「GB2312」,一般都是指「EUC-CN」表示法。
每一個漢字及符號以兩個字節來表示。第一個字節稱爲「高位字節」,第二個字節稱爲「低位字節」。
  「高位字節」使用了0xA1-0xF7(把01-87區的區號加上0xA0),「低位字節」使用了0xA1-0xFE(把01-94加上0xA0)。

  因爲一級漢字從16區起始,漢字區的「高位字節」的範圍是0xB0-0xF7,「低位字節」的範圍是0xA1-0xFE,佔用的碼位是72*94=6768。其中有5個空位是D7FA-D7FE。
  例如「啊」字在大多數程序中,會以兩個字節,0xB0(第一個字節)0xA1(第二個字節)儲存。(與區位碼對比:0xB0=0xA0+16,0xA1=0xA0+1)。

  二、UTF-8

      UTF-8和gb系列編碼同樣,其編碼某個漢字產生的字節順序,由其編碼方案決定,不受CPU字節序的影響。不管一個漢字有多少個字節,它的字節序與編碼順序保持一致。

例如漢字」嚴」利用utf8編碼過程:

一、已知「嚴」的unicode編碼是4E25(100111000100101),根據utf8規則能夠得知其utf8編碼須要三個字節。

  即格式是「1110xxxx 10xxxxxx 10xxxxxx」

  第一個字節前三位表示了字符「嚴」被編碼成utf8後的編碼長度,有多長,則從左開始填多少個1,若是隻有1個字節,則第一個位爲0。

  對於編碼後大於1個字節的符號,第一個字節的第四位爲0,其餘字節前兩位均要求爲10。

二、從」嚴「的最後一個二進制位開始,依次從後向前填入格式中的x,多出的位補0。這樣就獲得了「嚴」的utf8編碼爲「11100100 10111000 10100101」,轉換成十六進制就是E4B8A5。

編碼示例過程參考的原文:http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html

從上述過程能夠看到,utf8的字節序已經由其編碼方案決定,不受CPU字節序影響。

  三、Unicode

    Unicode只是一個符號集,它只規定了符號的二進制代碼,卻沒有規定這個二進制代碼應該如何存儲。因此他沒有要求如何存儲編碼後的字節,也就受CPU字節序的影響。

    Unicode的具體實現包括UTF-1六、UTF-32(固然也包括UTF-8,但因爲其編碼方式和編碼後的字節序與其餘Unicode編碼實現有較大區別,因此單獨拿出來說解的)。

  四、總結

    一、網絡通信

      在實際的網絡通信中,網絡協議例如TCP是規定網絡字節序(Network Order)是大端。而針對漢字具體使用什麼編碼,通信雙方要麼提早約定好,要麼就須要在數據包中標識好漢字具體使用的編碼。

      若是在網絡通信中,涉及例如UTF16這樣區分大小端的編碼,除非按網絡協議要求採用大端模式是,不然也要事先約定好,或者在數據包中標識好使用的字節序模式。

    二、文件

      文件的也會存儲漢字,固然也要進行編碼。若是採用UTF-16這樣的有字節序模式區分的編碼,編碼規則要求能夠在文件頭部的BOM(Byte Order Mark)來標記。若是沒有標記,除非事先知道字節序的模式,不然只能大小端都試一遍。

 

  Unicode規範中推薦的標記字節順序的方法是BOM。BOM不是「Bill Of Material」的BOM表,而是Byte Order Mark。BOM是一個有點小聰明的想法:

  在Unicode編碼中有一個叫作」ZERO WIDTH NO-BREAK SPACE」的字符,它的編碼是FEFF。而FEFF在Unicode中是不存在的字符,因此不該該出如今實際傳輸中。UCS(Unicode的學名)規範建議咱們在傳輸字節流前,先傳輸字符「ZERO WIDTH NO-BREAK SPACE」。

  這樣若是接收者收到FEFF,就代表這個字節流是Big-Endian的;若是收到FFFE,就代表這個字節流是Little-Endian的。所以字符「ZERO WIDTH NO-BREAK SPACE」又被稱做BOM。

  UTF-8不須要BOM來代表字節順序,但能夠用BOM來代表編碼方式。字符「ZERO WIDTH NO-BREAK SPACE」的UTF-8編碼是EF BB BF。因此若是接收者收到以EF BB BF開頭的字節流,就知道這是UTF-8編碼了。

9、參考資源

  http://baike.baidu.com/view/1922338.htm

  http://www.cppblog.com/izualzhy/archive/2011/10/20/158784.html

  http://www.cnblogs.com/junsky/archive/2009/08/06/1540727.html(負數的二進制表示方法)

  http://www.cnblogs.com/augellis/archive/2009/09/29/1576501.html (sizeof)

  http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html(漢字編碼)

  http://djt.qq.com/article/view/658?ADTAG=email.InnerAD.weekly.20130902(漢字編碼)

相關文章
相關標籤/搜索