大小端儲存是什麼鬼?

2022-09-05 06:02:51

以下內容為本人的著作,如需要轉載,請宣告原文連結 微信公眾號「englyf」https://www.cnblogs.com/englyf/p/16656222.html


大小端儲存的劃分是為了解決長度大於一個位元組的資料型別內容在儲存地址上以不同順序分佈的問題。

比如16位元的short整形,32位元的int整形,64位元的long整形,它們在儲存地址上,其實最小的劃分單位是位元組,那麼高低位的位元組排列在從低到高的儲存地址上有什麼規定呢?

如果最高位的位元組資料存在最低地址上,而次高位的位元組資料按次序排列在次低的地址上,那麼這種儲存方式就叫大端儲存

如果最低位的位元組資料存在最低地址上,而次低位的位元組資料按次序排列在次低的地址上,那麼這種儲存方式就叫小端儲存

那麼怎麼去判斷當前系統屬於大端儲存還是小端儲存呢?

判斷方法一:利用單位元組型別強制轉換多位元組型別變數獲取返回值比較

下面讓我們看看範例程式碼:

#include <iostream>

using namespace std;

bool IsSystemBigEndianStorage()
{
    short src = 1;
    char comp = (char)src;

    return (comp == 0);
}

int main()
{
    bool ret = IsSystemBigEndianStorage();
    if (ret) {
        cout << "big endian" << endl;
    } else {
        cout << "small endian" << endl;
    }

    return 0;
}

首先把單位元組範圍內的資料值(比如1)賦給更大長度的型別(比如2個位元組的short)變數src,然後利用單位元組長度的資料型別(char)強制轉換變數src,會在記憶體空間上擷取變數src對應儲存在最低地址的一個位元組資料並返回。

bool IsSystemBigEndianStorage()
{
    short src = 1;
    char comp = (char)src;

    return (comp == 0);
}

可以看到變數src的高位位元組資料為0,低位位元組資料為1,各不相同。

如果(char)src的返回值等於0,就表示儲存在最低地址的位元組資料等於高位位元組資料0x00,屬於大端儲存,否則表示屬於小端儲存

判斷方法二:利用聯合體型別union比較內部的單位元組資料

修改一下上面的函數IsSystemBigEndianStorage

bool IsSystemBigEndianStorage()
{
    union {
        short a;
        char b;
    } temp;
    temp.a = 1;

    return (temp.b == 0);
}

可以看到變數temp.a的高位位元組資料為0,低位位元組資料為1,各不相同。

根據記憶體空間中位元組對齊的規律,聯合體union型別,各成員變數的起始地址是一樣的。即使各成員變數的資料長度不一樣也不影響。

也就是說temp.a最低地址空間的資料內容就是temp.b的資料內容。

如果temp.b的值等於0,就表示儲存在最低地址的位元組資料等於高位位元組資料0x00,屬於大端儲存,否則表示屬於小端儲存

關於網路位元組順序

網路中充斥著各種各樣的終端裝置或者中間代理路由等,資料利用網路進行傳輸,傳輸的基本資料單位也是位元組,於是多位元組型別的資料也會面臨大小端的傳輸順序定義。

所以,在傳輸前和傳輸後的裝置怎麼同步這個多位元組型別資料的儲存呢?由傳輸前後端的裝置共同決定嗎?

比如兩個不同地區的人碰到一起,如果沒有約定俗成的共同語言,一樣不知如何去交流。

在資料成功傳送和解讀完整前,資料兩端的裝置不會理解對方的意圖,那麼就有必要由第三方來統一明確定義傳輸順序。

於是,TCP/IP 協定規定了網路傳輸多位元組型別資料時,先傳輸高位的位元組資料,次高位的位元組資料在其後接著傳輸。而資料在被網路介面傳送到網路時,需要從記憶體逐位元組讀取出來,從低地址往高地址開始傳送。那麼可見在網路傳輸中,資料的位元組順序形式是大端儲存。

本地資料怎麼和網路位元組順序轉換?

下面針對本地系統為linux舉個例子

從本地系統儲存順序轉換為網路位元組順序

uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);

從網路位元組順序轉換為本地系統儲存順序

uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);