計算機系統中的大端模式和小端模式

  最近工作中有用到一個知識點,就是大小端,當然這是一個小的知識點,爲什麼寫一個博文呢,我其實是想測試一下chatGPT,所以我開始將自己的想法告訴這個chatbot,讓他給我一些寫博文的建議,並且給我解答了一些疑惑,今天將自己的學習筆記整理出來展示給大家(by the way,一個有用的搜索引擎和chatbot對我們的工作有着事半功倍的作用,並且它的思路很明顯比我更好,如果只是單純的用在學習上,我覺得這個簡直是百科全書)。

  廢話不多說了,正文開始。

1,什麼是大小端?

  在計算機中,字節序指的是在存儲器中,多字節數據的字節存放順序。大小端是計算機體系結構中的一個概念,用於表示在多字節數據類型中,字節的順序。在不同的計算機體系結構中,字節順序可能不同。一些處理器將最高位字節存儲在地址最低的位置,這被稱爲“大端字節序”(高位字節排放在內存的低地址端,低位字節排放在內存的高地址端),而另一些處理器將最低位字節存儲在地址最低的位置,這被稱爲“小端字節序”(低位字節排放在內存的低地址端,高位字節排放在內存的高地址端)。

  例如,假設我們要存儲十六進制數0x12345678(十進制數爲305419896),在大端字節序下,它的存儲順序爲:

12 34 56 78

  而在小端字節下,則爲:

78 56 34 12

   以上從左到右=》 低地址(高位)到高地址(低位)。偷個圖:

 

2,大小端的歷史原因和發展

2.1 爲什麼會有大小端之分?

  大端和小端的概念最初由IBM的工程師Danny Cohen提出。這兩種字節序最初是由不同的計算機廠商開發出來的。早期的計算機在內部存儲器和CPU之間使用通信線,用於發送和接收數據。這些通信線被設計爲雙向的,可以在讀和寫時使用。因此,數據的字節順序是很重要的。在當時的計算機中,大端字節序被廣泛採用。

  這是由計算機體系結構的設計決定的。在早期的計算機設計中,內存是以字(word)爲單位進行讀寫的,字的長度不同,如16位、32位或64位等。在讀寫數據時,需要確定數據的字節順序,以便正確地處理數據。在計算機系統中,以字節爲單位,所以每個地址單元都對應一個字節,一個字節爲8bit。但是在C語言中除了8bit的char外,還有16bit的short,32bit的long。另外對於位數大於8位的處理器,例如16位或者32位的處理器,由於寄存器大於一個字節,那麼必然存在一個如何將多個字節安排的問題。因此就導致了大端存儲和小端存儲模式。

  在大端字節序中,最高有效位在最低的地址上,符合人類的習慣,容易理解和記憶。而在小端字節序中,最低有效位在最低的地址上,這種方式更容易實現,因爲它可以讓計算機在處理字節序列時不需要額外的轉換操作,只需要按照順序依次讀寫即可。

  實際上,不同的處理器架構和操作系統都有不同的字節序規則,例如x86架構的處理器使用小端字節序,而PowerPC架構的處理器使用大端字節序。因此,在進行數據傳輸或數據交換時,需要考慮數據的字節序,以確保數據的正確傳輸和解析。

2.2 大小端的發展歷史

  大小端(Endian)是指計算機中字節的排列順序。它分爲大端(Big-endian)和小端(Little-endian)。大端表示高位字節存儲在低地址,而小端表示低位字節存儲在低地址。大小端的發展歷史可以追溯到計算機的早期發展階段。

  1960s:大小端的概念最早可以追溯到20世紀60年代,IBM的System/360系列計算機採用了大端字節序。這種字節序的設計與人類閱讀數字的方式相似,即從高位到低位。

  1970s:在20世紀70年代,DEC公司推出了PDP-11系列計算機,採用了小端字節序。小端字節序的優勢在於,對於多字節整數的部分訪問和計算更加方便,因爲低位字節總是存儲在最低的地址。

  1980s:隨着計算機技術的發展,許多不同的處理器架構開始出現。例如,Motorola 68000系列處理器採用大端字節序,而Intel x86系列處理器採用小端字節序。這導致了計算機領域內的大小端之爭。

  1990s:在20世紀90年代,隨着網絡技術的普及,計算機之間的數據交換變得越來越重要。因此,網絡字節序(Network Byte Order)的概念應運而生。網絡字節序採用大端字節序,以確保不同處理器架構之間的數據交換能夠順利進行。

  2000s至今:在21世紀,計算機技術繼續發展,處理器架構也趨於多樣化。許多處理器,如ARM和PowerPC,支持大小端可配置,使得系統設計者可以根據實際需求選擇合適的字節序。此外,一些編程語言和庫也提供了跨平臺的字節序轉換功能,以便在不同字節序的系統之間進行數據交換。

  在早期的計算機體系結構中,大端字節序是主流的存儲方式。但是,後來隨着英特爾處理器的流行,小端字節序逐漸成爲主流。英特爾處理器採用小端字節序的主要原因是,其實現簡單且效率高。同時,小端字節序的處理方式也更符合人的直觀感覺,因爲我們通常是從低位到高位依次讀取數據。總之,大小端的發展歷史反映了計算機技術的演進和處理器架構的多樣化。如今,大小端問題已經不再是一個嚴重的障礙,因爲現代處理器和軟件都提供了靈活的解決方案來應對字節序差異。

  

3,大小端的實現原理和存儲方式

  在計算機中,大端(Big-Endian)和小端(Little-Endian)是兩種不同的數據存儲方式。下面我們詳細學習一下大小端的實現原理和存儲方式,具體定義如下:

  大端字節序(Big-Endian):在大端字節序中,數據的高位字節存儲在低位地址上,而數據的低位字節存儲在高位地址上。這種方式類似於我們人類習慣的順序,從左到右,高位到低位。

  存儲方式:假設我們有一個32位整數0x12345678,其內存地址爲0x1000。在大端字節序的系統中,該整數的存儲方式如下:

地址            數據
0x1000 -> 0x12 (高位字節)
0x1001 -> 0x34
0x1002 -> 0x56
0x1003 -> 0x78 (低位字節)

  

  小端字節序(Little-Endian):在小端字節序中,數據的低位字節存儲在低位地址上,而數據的高位字節存儲在高位地址上。這種方式與大端字節序相反,但是在實際應用中也有很多優點,例如可以方便地進行低位對齊。

  存儲方式:同樣以32位整數0x12345678爲例,其內存地址爲0x1000。在小端字節序的系統中,該整數的存儲方式如下:

地址            數據
0x1000 -> 0x78 (低位字節)
0x1001 -> 0x56
0x1002 -> 0x34
0x1003 -> 0x12 (高位字節)

  

  大小端的實現原理和存儲方式主要體現在多字節數據在內存中的排列順序。大端字節序將高位字節存儲在低地址,而小端字節序將低位字節存儲在低地址。不同的處理器架構可能採用不同的字節序,因此在進行跨平臺數據交換時需要注意字節序的轉換。兩種字節序的區別在於字節的存儲順序,而不是位的存儲順序。在多字節數據類型(例如整數、浮點數等)存儲時,其字節序決定了字節在內存中的排列順序。

  下面是一個示意圖,展示了一個32位整型數在大端和小端字節序下的存儲方式:

大端字節序:
0x12345678
+--------+
| 12 | 34|
+--------+
| 56 | 78|
+--------+

小端字節序:
0x12345678
+--------+
| 78 | 56|
+--------+
| 34 | 12|
+--------+

  

4,不同架構的字節序規則

  不同的處理器架構可能採用不同的字節序規則。以下是一些常見處理器架構及其字節序規則:

  Intel x86/x64(IA-32/IA-64): 字節序規則:小端(Little-endian) 說明:Intel x86和x64架構處理器廣泛應用於個人計算機和服務器領域。這些處理器採用小端字節序,將低位字節存儲在低地址。

  ARM: 字節序規則:大小端可配置(Configurable Endianness) 說明:ARM處理器廣泛應用於嵌入式系統和移動設備。ARM架構支持大小端可配置,系統設計者可以根據實際需求選擇合適的字節序。大多數情況下,ARM處理器默認採用小端字節序。

  MIPS: 字節序規則:大小端可配置(Configurable Endianness) 說明:MIPS處理器應用於嵌入式系統、網絡設備和遊戲機等領域。MIPS架構支持大小端可配置,允許系統設計者根據需求選擇字節序。通常情況下,MIPS處理器默認採用大端字節序。

  PowerPC: 字節序規則:大小端可配置(Configurable Endianness) 說明:PowerPC處理器應用於嵌入式系統、服務器和遊戲機等領域。PowerPC架構支持大小端可配置,允許系統設計者根據需求選擇字節序。通常情況下,PowerPC處理器默認採用大端字節序。

  Motorola 68000: 字節序規則:大端(Big-endian) 說明:Motorola 68000系列處理器曾廣泛應用於個人計算機、遊戲機和嵌入式系統等領域。這些處理器採用大端字節序,將高位字節存儲在低地址。

  SPARC: 字節序規則:大端(Big-endian) 說明:SPARC處理器主要應用於高性能計算和服務器領域。SPARC架構採用大端字節序,將高位字節存儲在低地址。

  需要注意的是,不同處理器架構的字節序規則可能會影響跨平臺數據交換。在進行數據交換時,需要注意字節序的轉換,以確保數據的正確性。許多編程語言和庫提供了跨平臺的字節序轉換功能,以便在不同字節序的系統之間進行數據交換。

  簡單將上面內容整理一下(方便直接看結論的童靴):

大小端 CPU
Big Endian PowerPC, MIPS, Mac OS, IBM , Sun, Mototola 68000, SPARC
Little Endian x86, x64, DEC, Windows

 

4.1  如何判斷機器的字節序

  遇到大小端的問題,我們如何解決呢,首先就是判斷機器的字節序(是大端字節序還是小端字節序),然後進行轉換。

  給一個C++判斷機器字節序的代碼:

// Judge the endian of the local system
inline bool isLittleEndian() {
union {
uint32_t i;
char c[4];
} test = {0x01020304};
return test.c[0] == 0x04;
}

  或者下面例子:

#include <iostream>

// 判斷機器字節序的函數
bool isLittleEndian() {
    int num = 1;
    char *ptr = reinterpret_cast<char*>(&num);
    return *ptr == 1;
}

int main() {
    if (isLittleEndian()) {
        std::cout << "This machine is Little-endian." << std::endl;
    } else {
        std::cout << "This machine is Big-endian." << std::endl;
    }
    return 0;
}

  下面是一個轉換大小端字節序的例子,比如你的機器需要大端,所以處理不同操作系統的時候,你需要考慮先判斷系統字節序的,然後不同的需要轉換,畢竟我們上面也提到了不同系統字節序不同。

#include <iostream>

// 轉換16位整數的字節序
uint16_t swapEndian16(uint16_t value) {
    return (value << 8) | (value >> 8);
}

// 轉換32位整數的字節序
uint32_t swapEndian32(uint32_t value) {
    return ((value << 24) & 0xFF000000) |
           ((value << 8) & 0x00FF0000) |
           ((value >> 8) & 0x0000FF00) |
           ((value >> 24) & 0x000000FF);
}

// 轉換64位整數的字節序
uint64_t swapEndian64(uint64_t value) {
    return ((value << 56) & 0xFF00000000000000) |
           ((value << 40) & 0x00FF000000000000) |
           ((value << 24) & 0x0000FF0000000000) |
           ((value << 8) & 0x000000FF00000000) |
           ((value >> 8) & 0x00000000FF000000) |
           ((value >> 24) & 0x0000000000FF0000) |
           ((value >> 40) & 0x000000000000FF00) |
           ((value >> 56) & 0x00000000000000FF);
}

int main() {
    uint16_t num16 = 0x1234;
    uint32_t num32 = 0x12345678;
    uint64_t num64 = 0x123456789ABCDEF0;

    std::cout << "Original 16-bit value: " << std::hex << num16 << std::endl;
    std::cout << "Swapped 16-bit value: " << std::hex << swapEndian16(num16) << std::endl;

    std::cout << "Original 32-bit value: " << std::hex << num32 << std::endl;
    std::cout << "Swapped 32-bit value: " << std::hex << swapEndian32(num32) << std::endl;

    std::cout << "Original 64-bit value: " << std::hex << num64 << std::endl;
    std::cout << "Swapped 64-bit value: " << std::hex << swapEndian64(num64) << std::endl;

    return 0;
}

  上面的示例展示瞭如何在C++中判斷機器的字節序以及如何轉換大小端字節序。在實際應用中,可能大家需要根據自己的需求對代碼進行調整和優化,我自己也是。(當然如果使用python,可能更方便)

5,應用場景

  大小端字節序在計算機系統中有很多應用場景。以下是一些常見的應用場景:
  1. 跨平臺數據交換: 當不同字節序的計算機系統需要交換數據時,需要進行字節序轉換以確保數據的正確性。例如,網絡協議中的數據包在發送和接收時需要進行字節序轉換。大多數網絡協議(如TCP/IP)採用大端字節序(網絡字節序)作爲標準。因此,在發送數據時,小端字節序的系統需要將數據轉換爲大端字節序;在接收數據時,需要將大端字節序的數據轉換爲小端字節序。
  2. 文件格式: 許多文件格式規定了數據的字節序。例如,BMP圖像文件格式規定了數據的字節序。在處理這些文件時,需要根據文件格式的要求進行字節序轉換,以確保數據的正確性。
  3. 數據庫存儲: 在數據庫中存儲多字節整數時,可能需要考慮字節序問題。不同的數據庫系統可能採用不同的字節序。在進行數據遷移或備份時,需要注意字節序的轉換。
  4. 外部設備通信: 在與外部設備(如傳感器、控制器等)通信時,可能需要進行字節序轉換。外部設備可能採用不同的字節序,因此在發送和接收數據時需要進行字節序轉換以確保數據的正確性。
  5. 二進制數據處理: 在處理二進制數據(如加密、壓縮等)時,可能需要考慮字節序問題。不同的算法可能對字節序有不同的要求。在實現這些算法時,需要根據算法的要求進行字節序轉換。
  總之,大小端字節序在計算機系統中有很多應用場景。在處理跨平臺數據交換、文件格式、數據庫存儲、外部設備通信和二進制數據處理等問題時,需要注意字節序的轉換,以確保數據的正確性。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章