
适用对象学过数据类型在内存中存放的同学本章目标彻底搞懂什么是大小端 如何用代码检测 为什么网络协议必须统一字节序引入为什么整数 0x1234567 在内存里不是顺序排放的想象一下你买了一套乐高积木包装盒上写着「适合 8 岁以上」。你打开一看里面有 7 个零件小、中、大、特大、超大、超超大、超超大-Plus以此类推。现在让你把这套乐高装进一个只能放 7 个格子的收纳盒。方案 A从左到右按「小→中→大→特大……」的顺序放方案 B从左到右按「超超大-Plus→……→中→小」的顺序放这两种方案都是把同样的 7 个零件装进同样的盒子但顺序完全相反。这就是「大小端」Endian的由来。第一部分大小端的定义1.1 概念引入假设有一个十六进制整数0x12 34 56 78四个字节它要存进内存。大端模式Big Endian把高位字节放在低地址低位字节放在高地址。换句话说大的放在前面低地址端所以叫 “Big Endian”。内存从左到右低地址 → 高地址地址: 0x00 0x01 0x02 0x03 数据: 0x12 0x34 0x56 0x78小端模式Little Endian把低位字节放在低地址高位字节放在高地址。换句话说小的放在前面低地址端所以叫 “Little Endian”。内存从左到右低地址 → 高地址地址: 0x00 0x01 0x02 0x03 数据: 0x78 0x56 0x34 0x12记忆口诀「大端 大字在小地址」「小端 小字在小地址」。1.2 生活中的比喻想象你写一本书作者简介页写着「张三生于 1998 年」。大端按自然阅读顺序写1998 → 19 → 1从年份高位写到低位小端从后往前写8 → 98 → 1998从日期低位写到高位两种写法都记录了同样的信息「1998 年」但写的顺序完全相反。1.3 为什么叫 “Endinn”“Endian” 这个词来自乔纳森·斯威夫特的《格列佛游记》小人国里两个党派因为吃鸡蛋应该从大端big end还是小端little end敲开而争论不休。这个典故生动地说明了两种方式记录的是同样的信息但顺序完全不同。第二部分为什么会有大小端2.1 历史原因大小端的分歧来自计算机硬件设计早期的决策。不同的 CPU 厂商选择了不同的内存存储方式CPU 类型字节序x86 / x64Intel、AMD小端大多数 ARM 处理器小端可配置为大端网络协议TCP/IP大端也叫「网络字节序」MIPS部分大端RISC-V小端标准规定2.2 字节序影响什么单字节的数据不受影响因为只有一个字节不存在「排列顺序」的问题。char、unsigned char等永远是一个字节存和取都一样。多字节数据才受影响所有大于 1 个字节的类型int、short、long、float、double在内存中的存储方式都和字节序有关。inta0x12345678;// 4 个字节// 小端机器0x78 在低地址0x12 在高地址// 大端机器0x12 在低地址0x78 在高地址2.3 为什么网络协议要用大端早期的网络设备来自不同厂商需要统一通信标准。如果各用各的字节序就像两个人用不同的语言说数字——「一百二十三」vs「三二一」所以TCP/IP 协议栈Socket 编程统一规定使用大端字节序传输数据。这叫「网络字节序Network Byte Order」。在发送数据之前要把本地字节序大端/小端转换成网络字节序大端收到数据后再转换回本地字节序。第三部分如何用代码检测大小端3.1 方法一指针类型转换#includestdio.hintcheck_endian(){unsignedintx0x1;// 0x00 00 00 01unsignedchar*p(unsignedchar*)x;// 强转为单字节指针if(*p1)// 看最低地址的那个字节是什么return1;// 返回 1 → 小端0x01 在低地址elsereturn0;// 返回 0 → 大端0x00 在低地址}intmain(){if(check_endian())printf(小端模式Little Endian\n);elseprintf(大端模式Big Endian\n);return0;}推演小端机器x 0x00 00 00 01低地址字节 0x01*p 1成立 → 小端。推演大端机器x 0x00 00 00 01低地址字节 0x00*p ! 1→ 大端。3.2 方法二联合体union共用内存联合体的所有成员共享同一块内存而且起始地址相同。用这个特性可以巧妙检测字节序#includestdio.hunionUn{inta;// 4 字节charc[4];// 同样 4 字节和 a 共用同一块内存};intmain(){unionUn un;un.a0x00000001;// 最低位字节为 1if(un.c[0]1)printf(小端\n);elseprintf(大端\n);return0;}推演小端机器un.a的内存布局 01 00 00 00un.c[0]读到的是01→ 小端。推演大端机器un.a的内存布局 00 00 00 01un.c[0]读到的是00→ 大端。union 为什么有效联合体的大小等于最大成员的大小所有成员从同一个起始地址开始存放。把int和char[4]放进同一个 union就相当于从int的角度和从char[4]的角度同时观察同一块内存字节序一目了然。第四部分大小端的实战影响4.1 文件格式与字节序某些文件格式如 BMP 图像、MP3的头部字段有固定的字节序要求。如果字节序不对图片打不开、音频播不了。// BMP 文件头的前 2 字节是 BM0x42 0x4D// 不受字节序影响单字节永远是 0x42 0x4D// 但 BMP 的像素数据宽度字段4 字节整数就受字节序影响// 小端机器写的是 0x00 00 04 00十进制 1024// 大端机器写的是 0x00 00 04 004.2 网络通信中的字节序转换POSIX 标准提供了四个转换函数在arpa/inet.h或ws2tcpip.h中#includearpa/inet.h// 本地字节序 → 网络字节序大端uint32_thtonl(uint32_thostlong);// host to network, long (32位)uint16_thtons(uint16_thostshort);// host to network, short (16位)// 网络字节序 → 本地字节序uint32_tntohl(uint32_tnetlong);// network to host, longuint16_tntohs(uint16_tnetshort);// network to host, short记忆口诀h host本地n network网络to在中间数字在最后短变短、长变长。典型用法发送结构化的网络数据包#includestdio.h#includearpa/inet.hintmain(){// 发送端假设是小端机器unsignedshortport8080;unsignedintip0xC0A80101;// 192.168.1.1unsignedshortnet_porthtons(port);// 转网络字节序unsignedintnet_iphtonl(ip);// 转网络字节序printf(本地 port: %u, 网络 port: %u\n,port,net_port);printf(本地 IP: 0x%x, 网络 IP: 0x%x\n,ip,net_ip);return0;}⚠️提醒如果你在 Windows 上写 Socket 程序arpa/inet.h可能找不到换winsock2.h或ws2tcpip.h。Linux / macOS 上用 POSIX 接口即可。4.3 不同字节序机器之间传输数据会怎样假设小端机器 A 向大端机器 B 发送整数0x123456784 字节原始数据。A 发送的字节流78 56 34 12按内存顺序B 收到后按大端解析0x78 56 34 12 十进制 2018915336完全错误的值这就是为什么字节序转换是不可省略的。第五部分经典面试题精讲面试题一什么是大小端如何用代码判断参考答案大端模式高位字节存放在低地址低位字节存放在高地址。小端模式低位字节存放在低地址高位字节存放在高地址。判断方法union 版本把int和char[4]放进 union给 int 赋值为0x00000001看char[0]的值。如果等于 1说明最低地址那个字节是 1就是小端如果等于 0就是大端。面试题二为什么 x86 是小端但网络协议用大端参考答案这是历史和实用性的权衡x86 采用小端是因为 CPU 设计时认为低位字节先处理更高效从低地址开始取指/取数硬件实现更简单网络协议用大端是因为 TCP/IP 协议诞生于 70 年代大端机器先出现加上大端在某些场景下打印数值更直观高位在前符合人类阅读习惯关键结论大小端只是硬件设计选择没有绝对的好坏但网络通信必须统一字节序所以有了htonl/ntohl这一套转换函数。面试题三下面这段代码在大小端机器上分别输出什么#includestdio.hintmain(){inta0x1234;char*p(char*)a;printf(%d\n,*p);// p 指向最低地址那个字节return0;}推演小端机器a 0x1234在内存中 34 12 00 00低地址在前*p 0x34 52大端机器a 0x1234在内存中 00 00 12 34*p 0x00 0答案小端机器输出 52大端机器输出 0。本章总结大小端对比表大端Big Endian小端Little Endian定义高位字节在低地址低位字节在低地址内存布局0x1234567812 34 56 7878 56 34 12x86/x64 平台❌✅Intel/AMD网络字节序✅TCP/IP 标准❌人类阅读习惯✅高位在前更直观❌检测大小端的两种方法方法原理代码核心指针强转把 int 指针强转为 char*读低地址字节*(char*)x 1→ 小端union 共用体int 和 char[4] 共用同一块内存un.c[0] 1→ 小端字节序转换函数函数含义场景htonshost → network short (16位)端口号htonlhost → network long (32位)IP 地址ntohsnetwork → host short接收端口号ntohlnetwork → host long接收 IP