Hi,大家好!最近工作中有同事問到字節序的問題,并且因為對字節序理解片面產生了一些編程上的問題。所以,借這個機會整理了這篇文章,希望能加深大家對字節序問題的理解。
- 我們平時在寫應用層程序時,一般不需要考慮字節序問題,因為字節序跟操作系統和硬件環境有關。而我們編寫的程序很多情況下不需要跨平臺,或者即使跨平臺大都是一樣的字節序(小端)。
- 但是當我們編寫網絡通信程序,就必須要考慮字節序問題,因為你的數據在這樣的場景下要跨平臺,必須解決不同系統、不同平臺的字節序問題。
- 本章目錄
1、什么是字節序
- 當字節數大于1時需要考慮字節序(只有一個字節的情況下,比如char類型,也就不存在順序問題啦)。
- 多字節數據在內存中被存儲為連續的字節序列,從低地址內存開始存儲。例如0x87654321在內存可以從低位到高位順序存儲,也可以從高位到地位順序存儲。
字節序分類
- 大端字節序所謂大端字節序就是從內存連續存儲時高位在前,低位在后。即低地址內存存高位字節,高地址內存存低位字節。
- 小端字節序所謂小端字節序就是從內存連續存儲時低位在前,高位在后。即低地址內存存低位字節,高地址內存存高位字節。
名字由來
"大端"和"小端"這兩個術語的由來據說源于《格列佛游記》(Gulliver's Travels)一書,作者是愛爾蘭作家喬納森·斯威夫特(Jonathan Swift),書中描繪了兩個敵對國家之間的爭議,該爭議起源于吃蛋的方式。
在書中,主人公吉列佛·蓋博是一個英國船長,他在一次航海中被風暴吹到了拉普圖,這是一個由兩個敵對國家組成的島嶼。這兩個國家是博爾納巴和利里巴。爭議的起因是如何打破硬煮蛋的問題,這導致了兩個國家的敵對。
這個寓言故事中的爭議象征著當時英國和法國之間的宗教和政治沖突,而在計算機科學領域,這個故事的概念被引用用來描述多字節數據在內存中的存儲方式,即大端字節序和小端字節序。
因此,"大端"和"小端"這兩個術語在計算機領域的使用,是借用了《格列佛游記》中的這個寓言故事,用來描述多字節數據中字節的存儲順序。
2、字節序優缺點
大端字節序(Big-Endian)和小端字節序(Little-Endian)是描述多字節數據在內存中存儲順序的兩種方式。
優缺點
大端字節序
優點:
易于閱讀:在內存中,數據的高位字節位于低地址,符合人類的閱讀習慣。
缺點:
不利于低級別的存儲與讀取:對于一些底層的操作,例如位操作,需要更多的指令來處理。
小端字節序
優點:
簡化低級別的操作:對于一些低級別的操作,例如在整數的最低位進行位操作,更加方便。
缺點:
閱讀困難:在內存中,數據的高位字節位于高地址,不符合人類的閱讀習慣,調試時可能會有一些困難。
選擇標準
- 硬件架構: 大部分個人計算機和服務器采用小端字節序,而一些大型機器和網絡設備采用大端字節序。
- 通信標準: 在網絡通信中,通常使用大端字節序(網絡字節序)。
- 處理器設計: 處理器的設計也會影響字節序的選擇。
前面說過,在實際應用中很多情況下字節序并不會引起問題,但在一些特定的場景,例如網絡通信、數據存儲和處理器指令集等方面,正確處理字節序是非常重要的。
3、常見系統字節序
常見的操作系統和芯片可以使用大端字節序(Big-Endian)或小端字節序(Little-Endian),這取決于它們的設計和架構。
操作系統
- Windows:
Windows x86和x86-64架構使用小端字節序。
- Linux:
大多數Linux系統(如x86和x86-64架構)采用小端字節序。
- macOS:
macOS運行在x86-64架構上,使用小端字節序。
- UNIX:
大多數UNIX系統,包括Solaris和AIX,也使用小端字節序。
芯片架構
- x86和x86-64:
這是Intel和AMD處理器常見的架構,它們使用小端字節序。
- ARM:
大多數ARM處理器使用小端字節序,但某些特定的ARM處理器支持可配置的字節序。
- MIPS:
MIPS處理器可以配置為使用大端或小端字節序,具體取決于硬件設計。
- PowerPC:
PowerPC處理器可以配置為使用大端或小端字節序,根據系統和應用需求。
4、系統字節序判斷
在C語言中,要判斷系統的字節序,一種常見的方法是通過檢查一個整數的存儲方式來確定。以下是一個簡單的示例:
#include <stdio.h>
int main() {
// 定義一個16位整數
unsigned short int num = 1;
// 將整數的地址轉換為字符指針
char *ptr = (char *)#
// 判斷字節序
if (*ptr == 1) {
printf("Little-Endian\n");
} else {
printf("Big-Endian\n");
}
return 0;
}
在這個例子中,我們定義了一個16位的整數 num
,然后通過將其地址轉換為字符指針 ptr
,我們可以檢查指針指向的內存中第一個字節的值來確定字節序。如果第一個字節的值是1,那么就是小端字節序;如果是0,則是大端字節序。
請注意,這種方法的可移植性可能不夠好,因為它依賴于編譯器的實現和系統的特定行為。更可靠的方法是使用頭文件中定義的預處理器宏,例如 <endian.h>
中的 BYTE_ORDER
宏。在這個頭文件中,BYTE_ORDER
可以是 __ORDER_LITTLE_ENDIAN__
、__ORDER_BIG_ENDIAN__
或 __ORDER_PDP_ENDIAN__
中的一個,分別表示小端、大端和PDP端字節序。以下是一個使用頭文件的示例:
#include <stdio.h>
#include <endian.h>
int main() {
#if BYTE_ORDER == __LITTLE_ENDIAN
printf("Little-Endian\n");
#elif BYTE_ORDER == __BIG_ENDIAN
printf("Big-Endian\n");
#else
printf("Unknown Endian\n");
#endif
return 0;
}
使用預處理器宏更可移植,因為它們是由編譯器提供的。更推薦此方法。
5、網絡字節序
網絡字節序(Network Byte Order)是一種標準化的字節序,用于在計算機網絡中傳輸數據。網絡字節序通常采用大端字節序(Big-Endian)。在網絡通信中,確保發送和接收端使用相同的字節序是非常重要的,以避免數據解釋錯誤。
以下是關于網絡字節序的一些詳解:
特點和標準
-
大端字節序: 在網絡字節序中,數據的高字節保存在內存的低地址,低字節保存在高地址。這種規定有助于不同體系結構的計算機在網絡上傳輸數據時能夠正確解釋字節序。
-
標準: 網絡字節序的標準由互聯網工程任務組(IETF)制定,它在網絡協議中被廣泛使用,例如在TCP和UDP協議的頭部中。
字節序轉換
在進行網絡通信時,為確保數據在不同主機之間正確解釋,可能需要進行字節序的轉換。通常,發送端在發送數據之前將其轉換為網絡字節序,而接收端在接收數據后將其轉換為本地字節序。
在C語言中,可以使用庫函數 htonl
、htons
、ntohl
、ntohs
來進行字節序的轉換:
htonl
(Host to Network Long):將32位整數由主機字節序轉換為網絡字節序。htons
(Host to Network Short):將16位短整數由主機字節序轉換為網絡字節序。ntohl
(Network to Host Long):將32位整數由網絡字節序轉換為主機字節序。ntohs
(Network to Host Short):將16位短整數由網絡字節序轉換為主機字節序。
示例:
#include <stdio.h>
#include <arpa/inet.h>
int main() {
uint32_t localValue = 0x12345678; // 305419896 in decimal
// 將主機字節序轉換為網絡字節序
uint32_t networkValue = htonl(localValue);
printf("Local Value: %08x\n", localValue);
printf("Network Value: %08x\n", networkValue);
// 將網絡字節序轉換為主機字節序
uint32_t convertedValue = ntohl(networkValue);
printf("Converted Value: %08x\n", convertedValue);
return 0;
}
在這個例子中,我們演示了將主機字節序轉換為網絡字節序和將網絡字節序轉換為主機字節序的過程。這里使用的是32位整數,但對于16位短整數,可以使用 htons
和 ntohs
來進行轉換。
6、tcp通信時send和recv
在TCP通信中,send
和 recv
函數通常不需要手動進行字節序的轉換。這是因為TCP協議本身并不關心數據的表示形式,它僅僅負責按照字節流的形式傳輸數據。TCP是面向字節流的協議,它不關心數據的具體結構,也不對數據進行解釋或處理。
字節序的問題通常涉及到在不同體系結構的計算機之間傳輸數據時,確保數據的正確解釋。TCP通信在傳輸數據時會將數據以字節流的形式傳輸,而不關心數據的具體結構,因此不需要在send
和recv
中進行字節序的轉換。
字節序的問題更常見于那些有特定數據結構的通信協議或文件格式,例如網絡協議頭部、數據包格式、文件格式等。在這些情況下,確保發送端和接收端對數據的解釋是一致的,就需要進行字節序的轉換。
在實際的網絡通信中,確保發送和接收端采用相同的字節序是非常重要的,但這通常是由高層協議或應用程序負責的,而不是由TCP協議本身負責。常用的解決方案是使用網絡字節序(大端字節序)進行數據傳輸,并在需要的時候進行字節序的轉換,以確保不同平臺之間的數據一致性。