1. 程式人生 > >吃雞蛋引發的血案,詳解內存中的字節序

吃雞蛋引發的血案,詳解內存中的字節序

跨平臺 ttl 應用層 轉化 gcc 中一 byte數組 else nio

吃雞蛋引發的血案,詳解內存中的字節序




技術分享圖片
傳送門: 柏鏈項目學院



??我們曾經看過一個饅頭引發的血案,那麽吃雞蛋也能引發血案嗎?確實能!英國作家喬納森·斯威夫特的《格列弗遊記》當中就記載了這樣的故事!

這是一場由於吃雞蛋引發的戰爭,戰爭開始是由於以下的原因:我們大家都認為,吃雞蛋前,原始的方法是打破雞蛋較大的一端。可是當今皇帝的祖父小時候吃雞蛋,一次按古法打雞蛋時碰巧將一個手指弄破了,因此他的父親,當時的皇帝,就下了一道敕令,命令全體臣民吃雞蛋時打破雞蛋較小的一端,違令者重罰。老百姓們對這項命令極為反感。歷史告訴我們,由此曾發生過六次叛亂,其中一個皇帝送了命,另一個丟了王位…關於這一爭端,曾出版過幾百本大部著作,不過大端派的書一直是受禁的,法律也規定該派的任何人不得做官。

??當然上述只是一個段子,吃雞蛋的問題不會影響那麽大,作者更多的是為了影射現實中因為很小的事情爆發大沖突的現象,比如天主教和新教之間的爭鬥!在IT世界,同樣存在類似的問題。我們都知道數據以字節形勢存儲,但是同樣的一段內存區域存儲相同的數據也是有兩個選擇的,因為內存地址有高有低之分,這就很像是吃雞蛋的問題了。下圖展示了2種選擇,第一種是將高位數據存放在低地址,低位數據存放在高地址,這比較符合人們讀數據從左到右的習慣,我們把這種方式叫大端法(bigendion);還有一種方式是將低位數據存放在低地址,高位數據存放在高地址,我們把這種方式叫小端法(littendtion)。

技術分享圖片

??有童鞋一定會說,出現2種選擇,一定很麻煩的,為什麽不能統一呢?小端法和大端法確實都存在,例如在網絡設備中就按照大端法的方式去識別,而在很多pc機器當中卻是按照小端法去識別,這就給我們編程增加了麻煩。作為程序員知道原理肯定是可以解決的!
按照正常邏輯,我們心中應該產生2個問題:第一個問題是我們使用的電腦是大端還是小端法?第二個問題是如何避免多平臺多主機間交互沒有問題?

??先來說第一個問題,這個很好驗證的!我們只需要驗證一下內存地址中低位存放的是低位數據還是高位數據就可以了!這個對於c程序員比較擅長,可以借助c語言的union特性來判斷。

#include <stdio.h>

int main()
{
    union {
        short a;
        char buf[sizeof(short)];
    } s;
    s.a = 0x0102;
    //printf("buf[0]=%d,buf[1]=%d\n",s.buf[0],s.buf[1]);
    if(s.buf[0] == 0x01) {
        printf("The system is bigendion\n");
    } else {
        printf("The system is littlendion\n");
    }
    return 0;
}

??因為union聯合體內的兩個元素共用相同的首地址,這樣a和buf可以認為在同一塊內存區域上。如果a(258)是0x0102,那麽就要看buf[0]和buf[1]內存放的是什麽數據?如果buf[0]是01就代表低位地址存放高位數據,這是大端,反之就是小端了!本機測試效果如下:

localhost:test yk$ gcc bigend.c -o bigend
localhost:test yk$ ./bigend 
The system is littlendion

??c語言作為更面向底層的語言,處理的肯定是更多的細節,如果是更為偏向應用層的語言,或許不知道這麽多也沒問題。在go語言中,大端法和小端法的處理仍然需要知道。go語言為我們提供了轉碼用的"encoding/binary"包,提供了將數字轉換為byte數組的方式,可以參看如下示例:

func IntToHex(num int64) []byte {
    buff := new(bytes.Buffer)
    err := binary.Write(buff, binary.BigEndian, num)
    if err != nil {
        log.Panic(err)
    }

    return buff.Bytes()
}

??IntToHex函數可以按照大端法的形勢將數據轉化為byte數組的形勢,這樣跨平臺的時候統一按照大端法解析就可以了!說到這裏是不是發現,我們前面提出的第二個問題也解決了!整理一下思路就是數據傳輸時按照統一的編碼格式進行編碼,接收方再按照相同的方式去解碼就不會出現問題了。
如果使用c語言或go語言這種偏向底層的語言編寫網絡程序,尤其是跨平臺的情況下,一定要懂得大端和小端的原理,否則可能要吃大虧!



技術分享圖片

吃雞蛋引發的血案,詳解內存中的字節序