大端,小端;网络序,主机序

📅 2026/7/6 21:31:36
大端,小端;网络序,主机序
在网络编程中常常遇到字节序问题。字节序指的是多字节数据类型在内存中存放的顺序高位保存在低地址还是高地址以此来划分大端还是小端。单字节数据类型一个数据就一个字节不涉及字节序的概念。内存的低地址保存数据的高位就是大段否则就是小端。1大端更符合我们直观的阅读顺序以及平时的习惯2大端小端指的是字节序大小端不同字节存储顺序是不一样的。大端小端并不影响一个字节内部的 bit 的顺序。对于一个字节的数据类型比如 char不受大端还是小端的影响。2无论是大端序还是小端序对于上边这个 int 类型的数据来书这个数据的地址都是 0x400即数据所在内存空间的低地址。用取地址返回的均是开始的地址。1 大端和小端大端和小端指的是 cpu 的属性常见的 intel 的 x86 cpu 是小端的mips 架构的 cpu 是大端的。假设有一个 int 类型的数据值为 0x12345678保存在地址 0x400 ~ 0x403 这 4 个字节的位置。大端在大端 cpu 上存储情况如下图所示高位存储在低地址。0x12 是高位存储在地址 0x400 上0x78 是低位存储才 0x403 上。小端小端与大端相反低位存储在高地址。0x12 是高位存储在 0x4030x78 是低位存储在 0x400。如何判断大端还是小端 如下代码可以判断大端还是小端使用 union 数据结构的特点union test 中有两个成员一个是 char 类型一个是 int 类型union 占用 4 个字节char 类型保存在 4 个字节的低地址。如果 a 是 0x12说明高位数据保存在了低地址是大端否则是小端。#include stdlib.h #include stdio.h #include string.h int main() { union test { char a; int b; }; union test t; t.b 0x12345678; if (t.a 0x12) { printf(大端\n); } else { printf(小端\n); } return 0; }程序编译之后运行打印小端与 lscpu 查看到的信息是一致的。如下代码如果在小端机器上输出为 0x1234如果在大端机器上输出为 0x5678。#include iostream int main() { union { unsigned a; struct { char b; short c; }d; }e{0x12345678}; std::cout e.d.c std::endl; return 0; }2 网络序和主机序网络序与大端序是一致的不管机器是大端的还是小端发向网络的数据以及从网络中接收的数据都是大端的。大端网络序与我们平时的思维习惯是一致的。网络序规定收到的数据的第一个字节是高位。在数据的发送侧是从低地址发送数据接收侧先到来的数据也是存储在低地址。所以高位存储在低地址网络序和大端是一致的。主机序与 cpu 有关可能是大端的也可能是小端的。3 转换函数linux 下 的 arpa/inet.h 头文件中提供了 4 个函数。4 个函数中的 h 表示 host主机序n 表示 net网络序l 针对的是 u32 的数据s 针对的是 u16 的数据。htonl 就是将 32 位的数据从主机序转化为网络序。uint32_t htonl(uint32_t hostlong);uint16_t htons(uint16_t hostshort);uint32_t ntohl(uint32_t netlong);uint16_t ntohs(uint16_t netshort);#include stdlib.h #include stdio.h #include string.h #include arpa/inet.h int main() { unsigned int a 0x12345678; unsigned short b 0x1234; printf(unsigned int 0x%x, host to net: 0x%x\n, a, htonl(a)); printf(unsigned int 0x%x, net to host: 0x%x\n, a, ntohl(a)); printf(unsigned short 0x%x, host to net: 0x%x\n, b, htons(b)); printf(unsigned short 0x%x, net to host: 0x%x\n, b, ntohs(b)); return 0; }怎么对 64 位数据进行字节序转换 如下代码实现了 64 位数据的字节序转换。最容易出错的是函数的最后一行 return (unsigned long)(((unsigned long)net_low 32) | net_high);其中 net_low 需要使用 unsigned long 进行转换否则会发生数据丢失。#include arpa/inet.h #include stdio.h #include sys/mman.h #include sys/resource.h uint64_t htonll(uint64_t val) { uint32_t low val 0xFFFFFFFF; uint32_t high (val 32) 0xFFFFFFFF; low htonl(low); high htonl(high); //low需要做转化否则移动之后数据丢失 return ((uint64_t)low 32) | high; } uint64_t ntohll(uint64_t val) { uint32_t low val 0xFFFFFFFF; uint32_t high (val 32) 0xFFFFFFFF; low ntohl(low); high ntohl(high); return ((uint64_t)low 32) | high; }