Big-Endian 與 Little-Endian 的差異與判斷程式碼

這裡介紹 Big-Endian 與 Little-Endian 兩種位元組順序的差異,並提供判斷位元組順序的 C 語言實作程式碼範例。

位元組順序(Endianness)是指資料在記憶體中的放置順序,不同的 CPU 可能會採用不同的放置規則,若遇到需要在不同機器或是網路之間交換低階的二進位資料時,就必須注意這個問題。

Big-Endian 與 Little-Endian

在現今主流的 CPU 中,最常見的位元組順序有兩種,分別是 Big-Endian 與 Little-Endian,Big-Endian 是指資料放進記憶體中的時候,最高位的位元組會放在最低的記憶體位址上,而 Little-Endian 則是剛好相反,它會把最高位的位元組放在最高的記憶體位址上。

我們直接以一個簡單的範例來說明 Big-Endian 與 Little-Endian 的差異會更清楚,假設我們有一個 32 位元(bits)的整數資料為 0x12345678,在 Big-Endian 的機器將其放置在記憶體中的時候,會按照下圖中的規則放置:

Big-Endian 範例(LibreOffice 原始檔

但是如果換成 Little-Endian 的機器,將 0x12345678 放在記憶體中的方式就會剛好相反,會變成這樣:

Little-Endian 範例

C 語言判斷位元組順序

如果我們的程式會使用 union 這類的低階存取方式處理記憶體中的資料,位元組順序就會是非常關鍵的問題,如果不確定自己的機器是屬於那一種位元組順序,可以使用以下這段簡單的程式來判斷:

#include <stdio.h>
typedef union {
  unsigned long l;
  unsigned char c[4];
} EndianTest;
int main() {
  EndianTest et;
  et.l = 0x12345678;
  printf("本系統位元組順序為:");
  if (et.c[0] == 0x78 && et.c[1] == 0x56 && et.c[2] == 0x34 && et.c[3] == 0x12) {
    printf("Little Endiann");
  } else if (et.c[0] == 0x12 && et.c[1] == 0x34 && et.c[2] == 0x56 && et.c[3] == 0x78) {
    printf("Big Endiann");
  } else {
    printf("Unknown Endiann");
  }
  printf("0x%lX 在記憶體中的儲存順序:n", et.l);
  for (int i = 0; i < 4; i++) {
    printf("%p : 0x%02Xn", &et.c[i], et.c[i]);
  }
  return 0;
}

在 Intel CPU 的電腦中執行的結果會類似這樣:

本系統位元組順序為:Little Endian
0x12345678 在記憶體中的儲存順序:
0x7fffbffafb10 : 0x78
0x7fffbffafb11 : 0x56
0x7fffbffafb12 : 0x34
0x7fffbffafb13 : 0x12

網路位元組順序

除了記憶體中的資料會有位元組順序的問題之外,在網路上傳輸的資料也有同樣的問題,在 IP 的通訊協定中,明確規定網路上傳輸的資料都使用 Big-Endian 的方式傳輸,如果我們需要將資料透過網路傳送時,可以使用 htonlhtonsntohlntohs 這幾個函數來處理本機與網路之間的位元組順序轉換。以下是一個簡單的範例:

#include <stdio.h>
#include <stdint.h>
#include <arpa/inet.h>
typedef union {
  uint32_t l;
  unsigned char c[4];
} EndianTest;

// 輸出位元組順序
void printBytes(uint32_t x) {
  EndianTest et;
  et.l = x;
  for (int i = 0; i < 4; i++) {
    printf("0x%02X ", et.c[i]);
  }
  printf("n");
}

int main() {
  uint32_t x = 0x12345678;
  printf("0x%X 在記憶體中的儲存順序:", x);
  printBytes(x);
  uint32_t n = htonl(x);
  printf("0x%X 在網路中的傳輸順序:", x);
  printBytes(n);
}
0x12345678 在記憶體中的儲存順序:0x78 0x56 0x34 0x12 
0x12345678 在網路中的傳輸順序:0x12 0x34 0x56 0x78
程式設計

5 留言

  1. kendrick

    您好, 請問以下這句是否為筆誤呢?
    “而 Little-Endian 則是剛好相反,它會把最低位的位元組放在最高的記憶體位址上。”
    Little-Endian 是最低位的位元組放在最*低*的記憶體位址上
    不知是否我的理解有錯呢, 謝謝。

    • G. T. Wang

      確實是我的筆誤,我已經修正了,感謝您的提醒。

  2. mike yang

    請問如果是bit field的話,以little-endian來說
    0x78 是從記憶體位址0x00往上看 也是由低位元排到高位元?
    0 0 0 1 ,1 1 1 0 ?
    8 7

    • Robinson Yeh

      應該是以一個位元為單位:
      0x78 => 0111 1000 (b)

      就直接把 0111 1000 (=120 in decimal)放在一格
      而非再把整個 0111 1000 翻轉變成 0001 1110

  3. leolarrel

    判斷一個byte 就行,不用四個byte 都判斷

Comments are Closed