前言

在嵌入式开发、网络通信、数据存储、驱动开发中,大小端问题是绕不开的核心知识点。很多新手遇到数据解析错误、串口收发乱码、多字节数据读写异常,90%都是大小端不匹配导致的。

本文用最通俗的语言+完整可运行代码+实战场景,带你彻底吃透大小端,干货满满!


一、什么是大小端?

1. 核心定义

大小端 = 计算机/MCU 存储 多字节数据 的字节顺序规则。

注意:

  • 单字节数据(char) 不存在大小端问题!
  • 只有 int、short、long、float 等多字节类型 才会区分大小端。

2. 两个关键概念

  • 大端模式(Big-Endian,BE)
    数据的高字节 存储在低地址,低字节存储在高地址。
    ✅ 符合人类阅读习惯。

  • 小端模式(Little-Endian,LE)
    数据的低字节 存储在低地址,高字节存储在高地址。
    ✅ 符合CPU运算机制,是主流嵌入式平台默认模式。


二、举个最经典的例子(一看就懂)

我们用一个 16位短整型数据 举例:

uint16_t data = 0x1234;

数据拆分:

  • 高字节:0x12
  • 低字节:0x34

假设存储起始地址:0x0000

1. 大端存储(BE)

内存地址0x0000(低地址)0x0001(高地址)
存储内容0x12(高字节)0x34(低字节)

2. 小端存储(LE)

内存地址0x0000(低地址)0x0001(高地址)
存储内容0x34(低字节)0x12(高字节)

32位数据例子

uint32_t data = 0x12345678;
  • 大端:12 34 56 78
  • 小端:78 56 34 12

三、常见平台的大小端规则

1. 小端模式(绝大多数嵌入式MCU)

  • ARM(STM32、NXP、GD32等全部Cortex-M内核)
  • x86 / x86_64(PC电脑、笔记本)
  • RISC-V

2. 大端模式

  • 老式PowerPC
  • 部分网络协议(网络字节序统一使用大端)
  • 某些专用DSP

重要结论

STM32开发 = 小端模式!
电脑CPU = 小端模式!
网络通信 = 必须转大端!


四、手写代码:判断当前设备是大端还是小端

这是嵌入式笔试、面试必考题目,代码可直接运行。

方法:利用联合体(union)特性

union 所有成员共享同一块内存,我们可以用char单字节读取int数据的第一个字节。

#include <stdio.h>
#include <stdint.h>

// 判断大小端函数
int check_endian(void)
{
    union {
        uint16_t data;
        uint8_t  bytes[2];
    } test;

    test.data = 0x1234;

    // 低地址存储的是低字节 0x34 → 小端
    if (test.bytes[0] == 0x34) {
        return 1;   // 小端
    }
    // 低地址存储的是高字节 0x12 → 大端
    else {
        return 0;   // 大端
    }
}

int main(void)
{
    if (check_endian()) {
        printf("当前设备:小端模式(Little-Endian)\n");
    } else {
        printf("当前设备:大端模式(Big-Endian)\n");
    }
    return 0;
}

运行结果(PC/STM32)

当前设备:小端模式(Little-Endian)

五、大小端转换函数(嵌入式必备工具)

在串口、CAN、以太网、SPI、I2C通信时,必须做大小端转换!

1. 16位数据大小端互换

uint16_t swap16(uint16_t data)
{
    return ((data << 8) & 0xFF00) | ((data >> 8) & 0x00FF);
}

2. 32位数据大小端互换

uint32_t swap32(uint32_t data)
{
    return ((data << 24) & 0xFF000000) |
           ((data << 8)  & 0x00FF0000) |
           ((data >> 8)  & 0x0000FF00) |
           ((data >> 24) & 0x000000FF);
}

示例演示

uint16_t a = 0x1234;
uint16_t b = swap16(a);  // b = 0x3412

uint32_t x = 0x12345678;
uint32_t y = swap32(x);  // y = 0x78563412

六、嵌入式实战:大小端导致的BUG(高频坑点)

场景1:串口发送16位数据

MCU(小端)发送 0x1234,直接强转char发送:

uint8_t buf[2];
uint16_t data = 0x1234;
buf[0] = ((uint8_t *)&data)[0];
buf[1] = ((uint8_t *)&data)[1];

实际发送:0x34 0x12
上位机 expecting:0x12 0x34
✅ 解决:发送前用 swap16() 转换

场景2:网络通信(Socket)

网络协议规定:网络字节序 = 大端
所以:

  • 发送:小端 → 大端
  • 接收:大端 → 小端

Linux/STM32 标准库函数:

htons()  // 主机转网络 16位
htonl()  // 主机转网络 32位
ntohs()  // 网络转主机 16位
ntohl()  // 网络转主机 32位

七、总结(面试/笔记必背)

  1. 单字节无大小端,多字节才有
  2. 大端:高字节存低地址(人类习惯)
  3. 小端:低字节存低地址(CPU/MCU主流)
  4. STM32/ARM/x86 = 小端
  5. 网络通信 = 必须使用大端
  6. 大小端不匹配 = 数据解析错误、通信乱码

结束语

大小端是嵌入式开发的基础核心知识点,理解原理+会写判断函数+会写转换函数,基本能应对99%的开发场景。

如果这篇文章对你有帮助,欢迎点赞、收藏、转发~
我会持续更新嵌入式干货!


更多推荐