同一串字节 12 34 56 78,按大端读是 0x12345678(305419896),
按小端读是 0x78563412(2018915346)。
看到这两个数字,很多人第一反应是「我读错了」。其实没错,只是字节序不同。
名字的由来比技术更有意思
「大端」「小端」这两个词出自 Jonathan Swift 的《格列佛游记》。书里有个国家, 国民为了「鸡蛋该从大的一头敲还是小的一头敲」分成两派,打了多年内战。
1970 年代,Danny Cohen 在写网络协议的文章时借用了这个典故,把两种字节顺序 分别叫 Big-Endian 和 Little-Endian。这个命名流传至今——技术圈难得有这么带文学梗的术语。
区别到底在哪
一个 32 位整数由 4 个字节组成。问题不是「哪个字节在前」,而是「哪个字节放低地址」。
以 0x12345678 为例(12 是最高有效字节,78 是最低):
- 大端:低地址放高字节 →
12 34 56 78 - 小端:低地址放低字节 →
78 56 34 12
大端的排列方式和人写字(左边是高位)一致,看起来更「自然」; 小端的好处是,把 32 位值强转成 8 位时,低地址那个字节正好就是想要的值, 早期硬件做类型转换更方便。
两种都活着,因为历史
这不是技术优劣问题,是历史包袱。x86 从诞生起就是小端, ARM 一开始也是小端(后来支持双端模式), 而 IBM、摩托罗拉、Sun 的架构多是大端。
结果就是:网络协议为了中立,规定统一用大端,也就是「网络字节序」。 TCP/IP 头部里的端口号、IP 地址,全是大端。
顺便一个常被问到的点:RISC-V 规范规定数据内存访问为小端, 所以处理器和 x86 在这一点上是一致的,但和网络数据交接时仍然要转换。
实际踩坑的三种场景
1. 解析二进制文件
文件格式本身规定了字节序,而且不同格式可能不一样:
- PNG、JPEG:大端
- BMP、WAV、GIF:小端
- ELF:跟随目标架构
这就是为什么 BMP 文件头里那个 42 4D("BM")之后跟着的文件大小,
在小端机器上读出来才是对的。
2. 读芯片寄存器
手册里给的通常是数值,比如「配置寄存器 = 0x0000A501」。但总线上是按字节传输的, 一次 8 位。这四个字节先发哪个?取决于芯片设计。
如果手册没写清楚,最简单的验证方法:写入一个已知值,再读回来, 看字节顺序对不对得上。
3. 跨架构传数据
x86 服务器写出的二进制文件,拿到大端设备上读,数值会完全错乱。 同理,网络抓包工具显示的字段顺序,和你按小端拼出来的结果,往往差一位。
浮点数也逃不掉
字节序影响的是「字节的排列」,而浮点数同样是多字节的,所以必然受影响。
同样是 12 34 56 78 四个字节:
- 按大端的单精度浮点读:约
5.69 × 10⁻²⁸ - 按小端的单精度浮点读:约
1.74 × 10³⁴
差了六十多个数量级,而且都是「合法的浮点数」,不会报错。这类 bug 最难查, 因为程序不崩溃,只是结果离谱。
怎么快速判断
几个实用技巧:
- 看数值是否在合理范围。如果本该是几百的数算出了 10³⁸,八成是字节序反了
- 看字节是否「镜像」。
12 34 56 78变成78 56 34 12是最典型的特征 - 查协议规范。凡是涉及网络的,默认先按大端试
- 拿一个已知值做对照。比如写入 1.0,看字节是不是
3F 80 00 00(大端)或00 00 80 3F(小端)
工具里我做了「交换字节序」按钮,就是把整串字节倒过来。 排查时先点一下看看结果合不合理,比在脑子里换算快得多。