亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

淺談字節(jié)序(Byte Order)及其相關(guān)操作

系統(tǒng) 2426 0

淺談字節(jié)序(Byte Order)及其相關(guān)操作 - Jeffrey Zhao - 博客園

淺談字節(jié)序(Byte Order)及其相關(guān)操作

2010-02-10 23:05 by Jeffrey Zhao, 12152 閱讀, 41 評(píng)論, 收藏 , 編輯

最近在為 Tokyo Tyrant 寫(xiě)一個(gè).NET客戶端類庫(kù)。Tokyo Tyrant公開(kāi)了一個(gè)基于TCP協(xié)議的二進(jìn)制協(xié)議,于是我們的工作其實(shí)也只是按照協(xié)議發(fā)送和讀取一些二進(jìn)制數(shù)據(jù)流而已,并不麻煩。不過(guò)在其中涉及到了“字節(jié)序”的概念,這本是計(jì)算機(jī)體系結(jié)構(gòu)/操作系統(tǒng)等課程的基礎(chǔ),不過(guò)我還是打算在這里進(jìn)行簡(jiǎn)單說(shuō)明,并且對(duì).NET中部分類庫(kù)在此類數(shù)據(jù)流處理時(shí)的注意事項(xiàng)進(jìn)行些許記錄與總結(jié)。

字節(jié)序(Byte Order)

說(shuō)到程序間的通信,說(shuō)到底便是發(fā)送數(shù)據(jù)流。我們一般把字節(jié)(byte)看作是數(shù)據(jù)的最小單位。當(dāng)然,其實(shí)一個(gè)字節(jié)中還包含8個(gè)比特(bit)──有時(shí)候我奇怪為什么很多朋友會(huì)不知道bit或是它和byte的關(guān)系。當(dāng)我們拿到一系列byte的時(shí)候,它本身其實(shí)是沒(méi)有意義的,有意義的只是“識(shí)別字節(jié)的方式”。例如,同樣4個(gè)字節(jié)的數(shù)據(jù),我們可以把它看作是1個(gè)32位整數(shù)、 2個(gè)Unicode、 或者字符4個(gè)ASCII字符。

同樣我們知道,在一個(gè)32位的CPU中“字長(zhǎng)”為32個(gè)bit,也就是4個(gè)byte。在這樣的CPU中,總是以4字節(jié)對(duì)齊的方式來(lái)讀取或?qū)懭雰?nèi)存,那么同樣這4個(gè)字節(jié)的數(shù)據(jù)是以什么順序保存在內(nèi)存中的呢?例如,現(xiàn)在我們要向內(nèi)存地址為a的地方寫(xiě)入數(shù)據(jù)0x0A0B0C0D,那么這4個(gè)字節(jié)分別落在哪個(gè)地址的內(nèi)存上呢?這就涉及到字節(jié)序的問(wèn)題了。

每個(gè)數(shù)據(jù)都有所謂的“有效位(significant byte)”,它的意思是“表示這個(gè)數(shù)據(jù)所用的字節(jié)”。例如一個(gè)32位整數(shù),它的有效位就是4個(gè)字節(jié)。而對(duì)于0x0A0B0C0D來(lái)說(shuō),它的有效位從高到低便是0A、0B、0C及0D——這里您可以把它作為一個(gè)256進(jìn)制的數(shù)來(lái)看(相對(duì)于我們平時(shí)所用的10進(jìn)制數(shù))。

而所謂 大字節(jié)序(big endian) ,便是指其“ 最高有效位(most significant byte) ”落在低地址上的存儲(chǔ)方式。例如像地址a寫(xiě)入0x0A0B0C0D之后,在內(nèi)存中的數(shù)據(jù)便是:

Big Endian

而對(duì)于 小字節(jié)序(little endian) 來(lái)說(shuō)就正好相反了,它把“ 最低有效位(least significant byte) ”放在低地址上。例如:

Little Endian

對(duì)于我們常用的CPU架構(gòu),如Intel,AMD的CPU使用的都是小字節(jié)序,而例如Mac OS以前所使用的Power PC使用的便是大字節(jié)序(不過(guò)現(xiàn)在Mac OS也使用Intel的CPU了)。此外,除了大字節(jié)序和小字節(jié)序之外,還有一種很少見(jiàn)的中字節(jié)序(middle endian),它會(huì)以2143的方式來(lái)保存數(shù)據(jù)(相對(duì)于大字節(jié)序的1234及小字節(jié)序的4321)。

關(guān)于字節(jié)序的詳細(xì)說(shuō)明,您可以參考Wikipedia里的 Endianness條目

相關(guān).NET類庫(kù)

BinaryWriter和BinaryReader

在.NET框架操作數(shù)據(jù)流的時(shí)候,我們往往會(huì)使用BinaryWriter和BinaryReader進(jìn)行讀寫(xiě)。這兩個(gè)類中都有對(duì)應(yīng)的WriteInt32或是ReadInt32方法,那么它們是如何處理字節(jié)序的呢?從MSDN上我們了解到 BinaryReader使用小字節(jié)序讀取數(shù)據(jù) 。這意味著:

          
            var 
          
          stream = 
          
            new 
          
          
            MemoryStream
          
          (
          
            new byte
          
          [] { 4, 1, 0, 0 });

          
            var 
          
          reader = 
          
            new 
          
          
            BinaryReader
          
          (stream);

          
            int 
          
          i = reader.ReadInt32(); 
          
            // i == 260
          
        

與之類似,自然BinaryWriter也是使用小字節(jié)序來(lái)寫(xiě)入數(shù)據(jù)。

BitConverter

有時(shí)候我們還會(huì)使用BitConverter來(lái)轉(zhuǎn)化byte數(shù)組及一個(gè)32位整數(shù)(自然也包括其他類型),這也是涉及到字節(jié)序的操作,那么它們又是如何處理的呢?與BinaryWriter和BinaryReader的“固定策略”不同,BitConverter的行為是平臺(tái)相關(guān)的。

首先,BitConverter有一個(gè)只讀靜態(tài)字段IsLittleEndian,它表示當(dāng)前平臺(tái)的字節(jié)序。由于我們?yōu)椴煌腃PU會(huì)安裝不同的.NET類庫(kù),因此您現(xiàn)在如果通過(guò).NET Reflector來(lái)查看這個(gè)字段會(huì)發(fā)現(xiàn)它被設(shè)置為一個(gè)常量true。那么接下來(lái),BitConverter上的各個(gè)方便便會(huì)根據(jù)IsLittleEndian的值產(chǎn)生不同行為了,例如它的ToInt32方法:

          
            public static unsafe int 
          
          ToInt32(
          
            byte
          
          [] value, 
          
            int 
          
          startIndex)
{
    
          
            // ...

    
          
          
            fixed 
          
          (
          
            byte
          
          * numRef = &(value[startIndex]))
    {
        
          
            if 
          
          ((startIndex % 4) == 0)
        {
            
          
            return 
          
          *(((
          
            int
          
          *)numRef));
        }
        
          
            if 
          
          (IsLittleEndian)
        {
            
          
            return 
          
          numRef[0] | (numRef[1] << 8) | (numRef[2] << 16) | (numRef[3] << 24);
        }

        
          
            return 
          
          (numRef[0] << 24) | (numRef[1] << 16) | (numRef[2] << 8) | numRef[3];
    }
}
        

顯然,這里會(huì)根據(jù)IsLittleEndian返回不同的值。

判斷當(dāng)前平臺(tái)的字節(jié)序

在.NET Framework中BitConverter.IsLittleEndian字段是一個(gè)常量,也就是說(shuō)它在編譯期便寫(xiě)入了一個(gè)靜態(tài)的值。那么我們?nèi)绻胍ㄟ^(guò)代碼來(lái)判斷當(dāng)前平臺(tái)的字節(jié)序,又該怎么做呢?其實(shí)這很簡(jiǎn)單:

          
            static unsafe bool 
          
          IsLittleEndian()
{
    
          
            int 
          
          i = 1;
    
          
            byte
          
          * b = (
          
            byte
          
          *)&i;
    
          
            return 
          
          b[0] == 1;
}
        

這里我們通過(guò)檢查32位整數(shù)1的第一個(gè)字節(jié)來(lái)確定當(dāng)前平臺(tái)的字節(jié)序。當(dāng)然,我們也可以使用其他類型,例如:

          
            static unsafe bool 
          
          AmILittleEndian()
{
    
          
            // binary representations of 1.0:
    // big endian: 3f f0 00 00 00 00 00 00
    // little endian: 00 00 00 00 00 00 f0 3f
    // arm fpa little endian: 00 00 f0 3f 00 00 00 00
    
          
          
            double 
          
          d = 1.0;
    
          
            byte
          
          * b = (
          
            byte
          
          *)&d;
    
          
            return 
          
          (b[0] == 0);
}
        

這段代碼來(lái)自mono的BitConverter類庫(kù),至于它為什么使用double而不是int,我也不是很清楚。

Buffer.BlockCopy方法

.NET類庫(kù)中自帶一個(gè) Buffer.BlockCopy 方法,它的作用是將一個(gè)數(shù)組的字節(jié)——不是元素——復(fù)制到另一個(gè)數(shù)組中去。換句話說(shuō),一個(gè)長(zhǎng)度為100的int數(shù)組經(jīng)過(guò)完整的復(fù)制后,就變成了長(zhǎng)度為50的long數(shù)組,因?yàn)橐粋€(gè)int為4字節(jié),而long為8字節(jié)。從文檔上看,Buffer.BlockCopy是與字節(jié)序相關(guān)的,也就是說(shuō),同樣的.NET代碼在字節(jié)序不同的平臺(tái)上得到的結(jié)果可能不同。因此,我建議在使用這個(gè)方法的時(shí)候多加小心。

面向特定字節(jié)序編程

我們知道,BitConverter的工作結(jié)果是和當(dāng)前平臺(tái)的字節(jié)序相關(guān)的,但是在很多時(shí)候,尤其是根據(jù)某個(gè)公開(kāi)的協(xié)議進(jìn)行通信編程的時(shí)候,是需要固定一個(gè)字節(jié)序的。例如Tokyo Tyrant便要求每個(gè)整數(shù)都以大字節(jié)序的方式來(lái)通信——無(wú)論是發(fā)送還是讀取。為了保證.NET代碼的平臺(tái)無(wú)關(guān)性,我們不能直接使用BitConverter.GetBytes或ToInt32方法進(jìn)行轉(zhuǎn)化。那么我們?cè)撛趺崔k呢?最直觀的方法自然是手動(dòng)進(jìn)行轉(zhuǎn)換:

          
            static int 
          
          ReadInt32(
          
            Stream 
          
          stream)
{
    
          
            var 
          
          buffer = 
          
            new byte
          
          [4];
    stream.Read(buffer, 0, 4);

    
          
            return 
          
          buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
}
        

由于我們可以通過(guò)BitConverter.IsLittleEndian來(lái)得到當(dāng)前平臺(tái)的字節(jié)序,我們也可以用它進(jìn)行判斷:

          
            static int 
          
          ReadInt32(
          
            Stream 
          
          stream)
{
    
          
            var 
          
          buffer = 
          
            new byte
          
          [4];
    stream.Read(buffer, 0, 4);

    
          
            if 
          
          (
          
            BitConverter
          
          .IsLittleEndian)
    {
        
          
            Array
          
          .Reverse(buffer);
    }

    
          
            return 
          
          
            BitConverter
          
          .ToInt32(buffer, 0);
}


          
            static void 
          
          WriteInt32(
          
            Stream 
          
          stream, 
          
            int 
          
          value)
{
    
          
            var 
          
          buffer = 
          
            BitConverter
          
          .GetBytes(value);

    
          
            if 
          
          (
          
            BitConverter
          
          .IsLittleEndian)
    {
        
          
            Array
          
          .Reverse(buffer);
    }

    stream.Write(buffer, 0, buffer.Length);
}
        

此外,我們知道BinaryWriter和BinaryReader都是依據(jù)小字節(jié)序進(jìn)行讀寫(xiě)的,因此我們也可以利用這點(diǎn)來(lái)讀寫(xiě)數(shù)據(jù)流。要不,接下來(lái)就由您試試看如何?

淺談字節(jié)序(Byte Order)及其相關(guān)操作


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦?。。?/p>

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 久久精品影院永久网址 | 国产欧美亚洲精品第二区首页 | 欧美体内she精视频毛片 | 4虎永免费最新永久免费地址 | 五月婷婷激情在线 | 四虎影院最新网址 | 天天爽天天碰狠狠添 | 欧美三级一区二区三区 | 国产亚洲欧美日韩国产片 | 色综合久久中文 | 草的我好爽的网站 | 亚洲精品国产一区二区三 | 国产精品久久久久久网站 | 五月婷婷综合激情网 | 奇米影视在线视频 | 精品久久久久亚洲 | 久久综合久久综合久久综合 | 中文字幕一区二区三区亚洲精品 | free性欧美video69 | 久久综合中文字幕一区二区三区 | 国产午夜爽爽窝窝在线观看 | 久久99精品久久久久久臀蜜桃 | 亚洲在线中文 | 欧美日韩国产在线观看 | 奇米成人影视 | 国产不卡福利 | 俄罗斯美女逼 | 四虎影视1515hh四虎免费 | 精品视频久久久 | 日韩一区二区在线免费观看 | 色噜噜狠狠狠狠色综合久一 | 国产成人+亚洲欧洲 | 国产精品免费网站 | 色综合视频在线 | 精品久久亚洲一级α | 一级做a爱片特黄在线观看 一级做a爱片特黄在线观看免费看 | 国产精品suv一区二区 | 奇米影视7777久久精品人人爽 | 成年美女| 日本免费成人网 | 看看的在线视频国产 |