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

戰爭熱誠發表於2023-05-06

  最近工作中有用到一個知識點,就是大小端,當然這是一個小的知識點,為什麼寫一個博文呢,我其實是想測試一下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. 二進位制資料處理: 在處理二進位制資料(如加密、壓縮等)時,可能需要考慮位元組序問題。不同的演算法可能對位元組序有不同的要求。在實現這些演算法時,需要根據演算法的要求進行位元組序轉換。
  總之,大小端位元組序在計算機系統中有很多應用場景。在處理跨平臺資料交換、檔案格式、資料庫儲存、外部裝置通訊和二進位制資料處理等問題時,需要注意位元組序的轉換,以確保資料的正確性。

相關文章