這裡介紹 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 的方式傳輸,如果我們需要將資料透過網路傳送時,可以使用 htonl、htons、ntohl 與 ntohs 這幾個函數來處理本機與網路之間的位元組順序轉換。以下是一個簡單的範例:
#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
