這裡介紹 Big-Endian 與 Little-Endian 兩種位元組順序的差異,並提供判斷位元組順序的 C 語言實作程式碼範例。
位元組順序(Endianness)是指資料在記憶體中的放置順序,不同的 CPU 可能會採用不同的放置規則,若遇到需要在不同機器或是網路之間交換低階的二進位資料時,就必須注意這個問題。
在現今主流的 CPU 中,最常見的位元組順序有兩種,分別是 Big-Endian 與 Little-Endian,Big-Endian 是指資料放進記憶體中的時候,最高位的位元組會放在最低的記憶體位址上,而 Little-Endian 則是剛好相反,它會把最高位的位元組放在最高的記憶體位址上。
我們直接以一個簡單的範例來說明 Big-Endian 與 Little-Endian 的差異會更清楚,假設我們有一個 32 位元(bits)的整數資料為 0x12345678
,在 Big-Endian 的機器將其放置在記憶體中的時候,會按照下圖中的規則放置:
但是如果換成 Little-Endian 的機器,將 0x12345678
放在記憶體中的方式就會剛好相反,會變成這樣:
如果我們的程式會使用 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 的方式傳輸,如果我們需要將資料透過網路傳送時,可以使用 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