62、大端与小端:如何判断当前系统的字节序?
字节序这个问题,说白了就是「多字节数据在内存里怎么排队」。
我记得刚入行那会儿,第一次在ARM板子上做串口通信协议解析,明明发送端发的是0x12345678,我这边收到的却是0x78563412。当时我还以为是硬件坏了,折腾了一整天……后来老同事过来看了一眼,说:「小子,你遇到大小端问题了。」
嗯,从那以后,字节序就成了我写底层代码时第一个要确认的事情。
什么是大端和小端?
先看一个32位整数:0x12345678。它在内存中占4个字节:
- 大端模式(Big-Endian):高位字节存低地址。也就是
0x12放在最前面。 - 小端模式(Little-Endian):低位字节存低地址。也就是
0x78放在最前面。
你想想看,这就像写数字。我们人类习惯从左到右写高位,这是大端思维。但CPU为了计算方便,很多都采用小端——因为低位先取出来做加法,效率更高。
| 地址 | 大端存储 | 小端存储 |
|---|---|---|
| addr+0 | 0x12 | 0x78 |
| addr+1 | 0x34 | 0x56 |
| addr+2 | 0x56 | 0x34 |
| addr+3 | 0x78 | 0x12 |
核心结论:大端符合人类阅读习惯,小端符合CPU计算习惯。没有谁对谁错,只有平台差异。
如何用C语言判断字节序?
方法其实很简单。我习惯用联合体(union)来判断,因为联合体的所有成员共享同一块内存起始地址。
#include <stdio.h>
int main() {
union {
unsigned int val; // 4字节
unsigned char bytes[4]; // 4字节
} test;
test.val = 0x12345678;
if (test.bytes[0] == 0x78) {
printf("小端模式\n");
} else if (test.bytes[0] == 0x12) {
printf("大端模式\n");
} else {
printf("未知字节序\n");
}
return 0;
}
为什么能这么判断?因为 bytes[0] 就是 val 在内存中的第一个字节。如果第一个字节是低位0x78,那就是小端;如果是高位0x12,那就是大端。
我个人习惯:把这个判断封装成一个宏或者内联函数,放在项目公共头文件里。每次移植代码到新平台,先调用一下,心里有底。
另一种方法:用指针强制转换
如果你不想用联合体,也可以用指针来读第一个字节:
#include <stdio.h>
int main() {
unsigned int val = 0x12345678;
unsigned char *p = (unsigned char *)&val;
if (*p == 0x78) {
printf("小端\n");
} else if (*p == 0x12) {
printf("大端\n");
}
return 0;
}
原理一样。把int的地址强转成char*,然后解引用,拿到的就是第一个字节。
常见平台字节序一览
| 平台/架构 | 字节序 |
|---|---|
| x86 / x86-64 | 小端 |
| ARM(默认) | 小端 |
| ARM(可配置) | 支持大端 |
| RISC-V | 小端 |
| PowerPC | 大端(可切换) |
| MIPS | 大端(可配置) |
| 网络协议(TCP/IP) | 大端(网络字节序) |
我曾经踩过的坑:有一次在STM32上做以太网通信,直接拿收到的网络数据包当int用,结果数值全反了。后来才想起来——网络字节序是大端,而Cortex-M默认是小端。必须用
ntohl() 做转换。
字节序对实际开发的影响
字节序问题主要出现在以下几个场景:
- 跨平台数据交换:比如PC和嵌入式设备通过串口或网络通信,双方字节序不同,数据就会乱。
- 二进制文件读写:在x86上写了一个二进制文件,拿到ARM上读,如果不做转换,数据全错。
- 协议解析:很多通信协议(如Modbus、CANopen)明确规定了字节序,必须按协议来。
- 强制类型转换:把char*强转成int*时,字节序会影响最终数值。
SVG:字节序判断流程图
避坑指南
- 不要假设平台字节序:哪怕你99%的项目都用小端,也请写一个判断。我曾经接手过一个项目,代码里写死了小端,结果客户要求移植到PowerPC上……嗯,改起来很痛苦。
- 网络编程一定要用转换函数:
htonl()、htons()、ntohl()、ntohs()这四个函数,该用就用,别偷懒。 - 结构体跨平台传输要小心:结构体成员的对齐和填充也会受字节序影响。我建议用序列化函数,逐字节打包。
- 联合体判断法在C99之后是标准行为:放心用,不是未定义行为。
一句话总结:字节序是底层开发的基本功。写代码前先搞清楚平台是什么序,能省掉你后面80%的调试时间。
公众号:蓝海资料掘金营,微信deep3321