国产免费AV|泡泡玛特欧洲总部将设在伦敦|中文天堂网www新版资源在线|一本久道综合在线中文|国精产品一二三产区的使用方法|香蕉鱼在线观看|www.27eee

 找回密碼
 注冊
搜索

程序開發中的大端與小端

[復制鏈接]
樓主
山海致遠 發表于 2013-11-19 00:43:36 | 只看該作者 |倒序瀏覽 |閱讀模式
  在計算機中是以字節為單位,每個地址對應一個字節,一個字節8bit。在C中,除了8bit的char以外,還有16bit的short,32位的int,64位long,當然具體要由編譯器決定,可以通過sizeof來獲取不同類型在內存中占用的字節數。在計算機系統中,當物理單位的長度大于1個字節時,就要區分字節順序。常見的字節順序有兩種:Big Endian(High-byte first)和Litter Endian(Low-byte first),當然還有其他字節順序,但不常見,例如Middle Endian。
一、最高有效位、最低有效位
  要理解Big Endian和Little Endian,首先要搞清楚MSB和LSB。
  1、MSB(Most Significant Bit)最高有效位
    在一個n位二進制數字中n-1位,也就是最左邊的位。
  2、LSB(Least Significant Bit)最低有效位
    指最右邊的位。
  例如:一個int類型的整型123456789
    二進制表達方式:0000 0111 0101 1011 1100 1101 0001 0101(從右向左,每4bit對齊,最左邊(高位)不夠用0補齊)
    十六進制表達方式:0 7 5 B C D 1 5
    按照上述關于MSB和LSB的意思,在二進制表達方式中,bit從0開始,從右向左,bit0為最低有效位,而bit23為最高有效位。而我們一般稱左邊的0x07為高位字節,0x15為低位字節。
    再通俗一點解釋就是:8421碼的,8這端為高位,1這端為低位,相應的字節則分別稱為高位字節和低位字節。
二、內存地址
  在內存中,多字節對象都是被存儲為連續的字節序列。例如在C語言中,一個類型為int的變量n,如果其存儲的首個字節的地址為0x1000,那么剩余3個字節的地址將存儲在0x1001~0x1003。總之,不管具體字節順序是以什么方式排列,內存地址的分配一般是從小到大的增長。我們常把0x1000稱為低地址端,把0x1003稱為高地址端。
三、大端和小端
  搞清楚MSB、LSB、高位字節、低位字節、內存地址之后,再理解大端和小端,就相當容易了,先看看概念:
    小端Little Endian:低字節存放在低地址,低位字節排放在內存的低地址端,高位字節排放在內存的高地址端。
    大端Big Endian:高字節存放在低地址,即高位字節排放在內存的低地址端,低位字節排放在內存的高地址端。
  以二節中的例子int類型整數123456789為例:
    小端在內存中排列:0x15 0xCD 0x5B 0x07 (低位在前)
    大端在內存中排列:0x07 0x5B 0xCD 0x15 (高位在前)
  從例子中可以看出小端比較符合人的思維,而大端則看上去非常直觀。
  注:
    1、例子中是假設編譯器支持int為32位的前提下,如果是16位,那大端的排列則為:0xCD 0x15 0x07 0x5B。
    2、大小端一般是由CPU架構決定,常見的Intel、AMD的CPU使用的是小端字節序,而PowerPC使用的是大端字節序,有些ARM處理器還可以選擇用大端還是小端模式,具體請自行查閱。
    3、c#中,字節序跟編譯平臺所在的CPU相關,例如在Intel x86 CPU架構的windows平臺中,c#采用的小端序。而Java由于其JVM屏蔽了不同CPU架構導致的字節序差異,所以默認采用大端字節序。所以,大小端模式是由CPU決定,而編譯器又可能會改變這種模式。
字節序內存地址int(16bit)int(32bit)特點
小端0x1001,0x1002,0x1003,0x10040x15 0xCD 0x5B 0x070x15 0xCD 0x5B 0x07低地址端存儲低位字節,低位在前
大端0x1001,0x1002,0x1003,0x10040xCD 0x15 0x07 0x5B0x07 0x5B 0xCD 0x15低地址端存儲高位字節,高位在前
四、網絡字節序和主機字節序
  網絡字節序(Network Order):TCP/IP各層協議將字節序定義為Big Endian,因此TCP/IP協議中使用的字節序通常稱之為網絡字節序。
  主機字節序(Host Order):整數在內存中保存的順序,它遵循Little Endian規則(不一定,要看主機的CPU架構)。所以當兩臺主機之間要通過TCP/IP協議進行通信的時候就需要調用相應的函數進行主機序列(Little Endian)和網絡序(Big Endian)的轉換。
  如果是做跨平臺開發時,雙方需要協商好字節序,然后根據程序運行的環境,確定是否需要字節序轉換。
例如約定的通訊字節序位Big Endian,默認的windows采用的Little Endian,那收到數據后就需要做轉換操作。
五、C#位操作符
  這里簡單記錄一下C#的位操作符,方便以后自己查閱,也方便理解后面的講解。
  1、按位與&
    1&0為0;0&0為0;1&1為1。
  2、按位或|
    1|0為1;0|0為0;1|1為1。
  3、按位取反~
    ~1為0;~0為1。
  4、按位異或^
    1^1為0;0^0為0;1^0為1。相等得0,相異等1。
  5、左移<<
    位左移運算,將整個數向左移若干位,左移后空出的部分用0補齊。
  6、右移>>
    位右移運算,將整個數向右移若干位,右移后空出的部分用0補齊。
六、C#中關于大端和小端的轉換
  1、重復輪子
  1. using System;

  2. namespace Framework.NetPackage.Common
  3. {
  4.     /// <summary>
  5.     /// 字節序轉換輔助類
  6.     /// </summary>
  7.     public static class Endian
  8.     {
  9.         public static short SwapInt16(this short n)
  10.         {
  11.             return (short)(((n & 0xff) << 8) | ((n >> 8) & 0xff));
  12.         }

  13.         public static ushort SwapUInt16(this ushort n)
  14.         {
  15.             return (ushort)(((n & 0xff) << 8) | ((n >> 8) & 0xff));
  16.         }

  17.         public static int SwapInt32(this int n)
  18.         {
  19.             return (int)(((SwapInt16((short)n) & 0xffff) << 0x10) |
  20.                           (SwapInt16((short)(n >> 0x10)) & 0xffff));
  21.         }

  22.         public static uint SwapUInt32(this uint n)
  23.         {
  24.             return (uint)(((SwapUInt16((ushort)n) & 0xffff) << 0x10) |
  25.                            (SwapUInt16((ushort)(n >> 0x10)) & 0xffff));
  26.         }

  27.         public static long SwapInt64(this long n)
  28.         {
  29.             return (long)(((SwapInt32((int)n) & 0xffffffffL) << 0x20) |
  30.                            (SwapInt32((int)(n >> 0x20)) & 0xffffffffL));
  31.         }

  32.         public static ulong SwapUInt64(this ulong n)
  33.         {
  34.             return (ulong)(((SwapUInt32((uint)n) & 0xffffffffL) << 0x20) |
  35.                             (SwapUInt32((uint)(n >> 0x20)) & 0xffffffffL));
  36.         }
  37.     }
  38. }
復制代碼


    2、BCL庫支持的函數
    System.Net.IPAddress.HostToNetworkOrder、System.Net.IPAddress.NetworkToHostOrder,這兩個函數的內部實現和上面重復輪子原理一模一樣。
七、關于負數
  在計算機中,負數以其絕對值的補碼形式表示,不明白可以查閱九中貼出的相關資源。關于負數的字節序跟一般整數的字節序處理沒有任何區別。
八、關于漢字編碼以及與字節序的關系
  1、對于gb2312、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)。
  2、UTF-8
      UTF-8和gb系列編碼一樣,其編碼某個漢字產生的字節順序,由其編碼方案決定,不受CPU字節序的影響。無論一個漢字有多少個字節,它的字節序與編碼順序保持一致。
例如漢字”嚴”利用utf8編碼過程:
1、已知“嚴”的unicode編碼是4E25(100111000100101),根據utf8規則可以得知其utf8編碼需要三個字節。
  即格式是“1110xxxx 10xxxxxx 10xxxxxx”
  第一個字節前三位表示了字符“嚴”被編碼成utf8后的編碼長度,有多長,則從左開始填多少個1,如果只有1個字節,則第一個位為0。
  對于編碼后大于1個字節的符號,第一個字節的第四位為0,其他字節前兩位均要求為10。
2、從”嚴“的最后一個二進制位開始,依次從后向前填入格式中的x,多出的位補0。這樣就得到了“嚴”的utf8編碼為“11100100 10111000 10100101”,轉換成十六進制就是E4B8A5。
編碼示例過程參考的原文:http://m.afoofa.cn/thread-1454-1-1.html
從上述過程可以看到,utf8的字節序已經由其編碼方案決定,不受CPU字節序影響。
  3、Unicode
    Unicode只是一個符號集,它只規定了符號的二進制代碼,卻沒有規定這個二進制代碼應該如何存儲。所以他沒有要求如何存儲編碼后的字節,也就受CPU字節序的影響。
    Unicode的具體實現包括UTF-16、UTF-32(當然也包括UTF-8,但由于其編碼方式和編碼后的字節序與其他Unicode編碼實現有較大區別,所以單獨拿出來講解的)。
  4、總結
    1、網絡通訊
      在實際的網絡通訊中,網絡協議例如TCP是規定網絡字節序(Network Order)是大端。而針對漢字具體使用什么編碼,通訊雙方要么提前約定好,要么就需要在數據包中標識好漢字具體使用的編碼。
      如果在網絡通訊中,涉及例如UTF16這樣區分大小端的編碼,除非按網絡協議要求采用大端模式是,否則也要事先約定好,或者在數據包中標識好使用的字節序模式。
    2、文件
      文件的也會存儲漢字,當然也要進行編碼。如果采用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編碼了。


您需要登錄后才可以回帖 登錄 | 注冊

本版積分規則

手機版|小黑屋|ELEOK |網站地圖

GMT+8, 2026-5-26 02:08

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

快速回復 返回頂部 返回列表