START
Hi,大家好!最近工作中有同事问到字节序的问题,并且因为对字节序理解片面产生了一些编程上的问题。所以,借这个机会整理了这篇文章,希望能加深大家对字节序问题的理解。
"大端"和"小端"这两个术语的由来据说源于《格列佛游记》(Gulliver's Travels)一书,作者是爱尔兰作家乔纳森·斯威夫特(Jonathan Swift),书中描绘了两个敌对国家之间的争议,该争议起源于吃蛋的方式。
在书中,主人公吉列佛·盖博是一个英国船长,他在一次航海中被风暴吹到了拉普图,这是一个由两个敌对国家组成的岛屿。这两个国家是博尔纳巴和利里巴。争议的起因是如何打破硬煮蛋的问题,这导致了两个国家的敌对。
这个寓言故事中的争议象征着当时英国和法国之间的宗教和政治冲突,而在计算机科学领域,这个故事的概念被引用用来描述多字节数据在内存中的存储方式,即大端字节序和小端字节序。
因此,"大端"和"小端"这两个术语在计算机领域的使用,是借用了《格列佛游记》中的这个寓言故事,用来描述多字节数据中字节的存储顺序。
大端字节序(Big-Endian)和小端字节序(Little-Endian)是描述多字节数据在内存中存储顺序的两种方式。
前面说过,在实际应用中很多情况下字节序并不会引起问题,但在一些特定的场景,例如网络通信、数据存储和处理器指令集等方面,正确处理字节序是非常重要的。
常见的操作系统和芯片可以使用大端字节序(Big-Endian)或小端字节序(Little-Endian),这取决于它们的设计和架构。
在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;
}
使用预处理器宏更可移植,因为它们是由编译器提供的。更推荐此方法。
网络字节序(Network Byte Order)是一种标准化的字节序,用于在计算机网络中传输数据。网络字节序通常采用大端字节序(Big-Endian)。在网络通信中,确保发送和接收端使用相同的字节序是非常重要的,以避免数据解释错误。
以下是关于网络字节序的一些详解:
在进行网络通信时,为确保数据在不同主机之间正确解释,可能需要进行字节序的转换。通常,发送端在发送数据之前将其转换为网络字节序,而接收端在接收数据后将其转换为本地字节序。
在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
来进行转换。
在TCP通信中,send
和 recv
函数通常不需要手动进行字节序的转换。这是因为TCP协议本身并不关心数据的表示形式,它仅仅负责按照字节流的形式传输数据。TCP是面向字节流的协议,它不关心数据的具体结构,也不对数据进行解释或处理。
字节序的问题通常涉及到在不同体系结构的计算机之间传输数据时,确保数据的正确解释。TCP通信在传输数据时会将数据以字节流的形式传输,而不关心数据的具体结构,因此不需要在send
和recv
中进行字节序的转换。
字节序的问题更常见于那些有特定数据结构的通信协议或文件格式,例如网络协议头部、数据包格式、文件格式等。在这些情况下,确保发送端和接收端对数据的解释是一致的,就需要进行字节序的转换。
在实际的网络通信中,确保发送和接收端采用相同的字节序是非常重要的,但这通常是由高层协议或应用程序负责的,而不是由TCP协议本身负责。常用的解决方案是使用网络字节序(大端字节序)进行数据传输,并在需要的时候进行字节序的转换,以确保不同平台之间的数据一致性。