這裡介紹 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 Endian\n");
  } else if (et.c[0] == 0x12 && et.c[1] == 0x34 && et.c[2] == 0x56 && et.c[3] == 0x78) {
    printf("Big Endian\n");
  } else {
    printf("Unknown Endian\n");
  }
  printf("0x%lX 在記憶體中的儲存順序:\n", et.l);
  for (int i = 0; i < 4; i++) {
    printf("%p : 0x%02X\n", &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