大端模式、小端模式及在QT中的轉換


轉載於:https://blog.csdn.net/usownh/article/details/42614185

大端模式和小端模式是計算機中經常涉及到的兩種字節序,也有大端對齊、小端對齊、大尾、小尾等叫法。

一、起源

說起這兩種模式,就不得不提一下大端(Big-endian)和小端(Little-endian)這兩個英文上的起源。

“endian”一詞來源於喬納森·斯威夫特的小說格列佛游記。Lilliput和Blefuscu這兩個強國在過去的36個月中一直在苦戰。戰爭的原因:大家都知道,吃雞蛋的時候,原始的方法是打破雞蛋較大的一端(Big-End),可以那時的皇帝的祖父由於小時侯吃雞蛋,按這種方法把手指弄破了,因此他的父親,就下令,命令所有的子民吃雞蛋的時候,必須先打破雞蛋較小的一端(Little-End),違令者重罰。然后老百姓對此法令極為反感,期間發生了多次叛亂,其中一個皇帝因此送命,另一個丟了王位,產生叛亂的原因就是另一個國家Blefuscu的國王大臣煽動起來的,叛亂平息后,就逃到這個帝國避難。據估計,先后幾次有11000余人情願死也不肯去打破雞蛋較小的端吃雞蛋。這個其實諷刺當時英國和法國之間持續的沖突。(引自http://blog.csdn.net/ce123_zhouwei/article/details/6971544)其中兩種方法吃雞蛋的人分別被稱為Big-endians和Little-endians。1980年,Danny Cohen在其著名的論文”On Holy Wars and a Plea for Peace”中,為平息一場關於字節該以什么樣的順序傳送的爭論,而引用了該詞。

二、存儲模式

接下來就說說為什么會有字節序的問題。

計算機在存儲數據的時候,是以字節(byte)為基本單位來存儲的,因此存儲單字節類型的數據(比如char)不存在字節序的問題。但存儲多字節的數據的時候(比方說4字節的int變量),就涉及到了以一個什么樣的順序來存儲。下面舉例來說明大端和小端的存儲方式。

定義變量 unsigned long long a=0x1122334455667788 

變量a是一個64位的無符號整數,共需要8個字節來存儲,那么在兩種模式下是如何存儲的呢?

||--1--||--2--||--3--||--4--||--5--||--6--||--7--||--8--||  地址

||  11 ||  22 ||  33 ||  44 ||  55 ||  66 ||  77 ||  88 ||  大端模式

||  88 ||  77 ||  66 ||  55 ||  44 ||  33 ||  22 ||  11 ||  小端模式

從中很容易可以看出各自的存儲特點。

三、需要注意的幾個問題

1.大端模式和小端模式是以基本類型為單位的

對於long long a 和 struct{ char a;short b;int c;}二者同樣占據了8個字節的空間,在存儲上,前者上面已經介紹,后者則是先存儲一個char,空一個字節,然后按照大端/小端模式存儲short,最后按照大端/小端模式存儲int。

2.大端模式與小端模式的實際應用范圍

在我們日常使用的x86架構的計算機中(其他類別的可能會采用大端模式或可配置模式,可以通過查閱資料或者用下文的代碼進行測試),都是使用的小端模式,而網絡字節序是大端模式的。這就使得在網絡通信時進行字節序的轉換變得極為重要。比方說,通信雙方規定了了通信頭為一個4字節的魔數(Magic Number),而一方按着大端序的模式發送,一方按着小端序的模式解讀,那么兩方的通信就會失敗。如果沒有這個魔數,而在內部的數據中出現這樣的問題則會更加的麻煩。

3.文件存儲中的模式

文件的存儲一般都是以字節來進行操作的,因此,在文件中以什么樣的字節序需要程序的編寫者加以注意。比方說下面的程序:

    int a=0x11223344;
    FILE *fp;
    fp=fopen("test","wb");
    fwrite(&a,sizeof(a),1,fp);
    fclose(fp);

  用十六進制編輯器打開文件之后,我們會發現文件的內容是44332211。原因很簡單,fwrite函數直接把內存中的內容按順序寫入了文件,因此內存中是小端模式存儲的,所以寫入文件也是小端模式。

四、優缺點

大端模式,由於符號位和數值的高位存在地址的低位,會優先被讀到,更容易先確定數據的重要信息。

小端模式,在進行類型轉換的時候不需要調整數據。如int強制轉換到char,計算機不需要做任何調整,直接讀取int的第一個字節即可。

五、大端和小端的檢測

對於大端模式和小端模式的檢測,可以利用上面所說的強制類型轉換。

bool isLittleEndian()
{
    short a=0x0061;
    if((char)a=='a') return true;
    else return false;
}

在查閱資料后,還發現了另外一個方法,利用到了被我遺忘很久的一個數據結構,聯合體union。

bool isLittleEndian()
{
    union
    {
        short a;
        char  b;
    }test;
    test.a=0x0061;
    if(test.b=='a') return true;
    else return false;
}

這個方法利用了聯合體共用內存的特性,因此回避了強制類型轉換。

六、Qt中大端小端的轉換

Qt中<QtEndian>包含了大端小端轉換的幾個函數

T    qFromBigEndian(const uchar * src)
T    qFromBigEndian(T src)
T    qFromLittleEndian(const uchar * src)
T    qFromLittleEndian(T src)
void    qToBigEndian(T src, uchar * dest)
T    qToBigEndian(T src)
void    qToLittleEndian(T src, uchar * dest)
T    qToLittleEndian(T src)

下面對這個幾個函數進行簡單的說明。

    union{
        int a;
        char b[4];
    }test1,test2;
    test1.a=0x61626364;
    test2.a=qFromBigEndian(test1.a);
    qDebug()<<test1.b[0]<<test1.b[1]<<test1.b[2]<<test1.b[3];
    qDebug()<<test2.b[0]<<test2.b[1]<<test2.b[2]<<test2.b[3];

對於qFromBigEndian()函數,它會判斷執行程序的主機的字節序,如果是大端模式的計算機,那么只是讀取數據,不進行轉換,如果是小端模式的計算機,那么則進行轉換。

因此我在本機(小端模式)上的的執行結果是:

d c b a

a b c d

可以看出,它將數據進行了轉換。

對於qFromLittleEndian()函數,和前者類似。對於大端模式的計算機進行轉換,對於小端模式的計算機只是讀取數據。

    union{
        int a;
        char b[4];
    }test1,test2;
    test1.a=0x61626364;
    test2.a=qFromLittleEndian(test1.a);
    qToBigEndian(test1.a,(uchar*)test2.b);
    qDebug()<<test1.b[0]<<test1.b[1]<<test1.b[2]<<test1.b[3];
    qDebug()<<test2.b[0]<<test2.b[1]<<test2.b[2]<<test2.b[3];

對於qToBigEndian()函數,也有着上面的規則,對於小端模式的計算機進行轉換,對於大端模式的計算機只進行讀取。

因此,本機(小端模式)的執行結果是:

d c b a

a b c d

對於qToLittleEndian()函數,只對大端模式的計算機進行轉換。

 

需要注意的是,Qt中的模板T只針對有符號和無符號的整型,對於浮點型(一般也不會用到),需要進行強制類型轉換。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM