一文读懂大小端:从原理到实践的深度剖析

推荐下我之前写的C/C++ OpenGL入门专栏,如果你想学习OpenGL,欢迎订阅,也可以通过底部的阅读原文进入


最近一次的项目组会议中涉及到了大小端的讨论,而后我又在读者交流群内发了条消息,想引发大家对于这个基本概念——大小端的思考。后来读者朋友想要让我写一篇文章来讨论下大小端的问题,遂作本文。

我写文章分析问题的通常有一个固定的思路,是什么(What)、为什么(Why)、怎么做/用(How)。本文也将按照这个思路进行。

1. 什么是大小端?

我们学习任何一门语言时,都需要了解支持的数据类型及其占用的字节数,在不考虑特殊平台/编译器(下同,本文均不考虑特殊平台、特殊编译器)的情况下:

  • char:占用1个字节;
  • int:占用4个字节;
  • long:占用4个字节;
  • float:占用4个字节;
  • double:占用8个字节;

我们可以看到除char外占用超过一个字节。我们知道内存以字节为单位存储数据,那内存中是如何存储大于一个字节的数据呢。 以int类型的数据为例,已知int类型数据占用四个字节,如存在数据int a = 0x12345678,a在内存中如何存储的呢?是从高位开始排,还是从低位开始排?

从低位排或者从高位排,这是两种字节序的讨论:

  • 大端(Big Endian):高位字节排在低地址处,低位字节排在高地址处。
  • 小端(Little Endian):低位字节排在低地址处,高位字节排在高地址处。

你可以把它类比为读书的顺序:

  • 大端更像是我们平常从左往右写字(高位在前)。
  • 小端则像是从右往左写字(低位在前)。

针对于上文提到的int型数据a,其大小端的存储方式如下:

通过该表格,可以看到:大端更符合我们的阅读习惯

2.为什么会有大小端?

为什么会出现两种存储类型呢,为什么当初没能够统一呢?出现两种存储类型,不仅增加了理解负担,而且增加了使用成本。

究其原因,是因为计算机体系结构的不同,导致不同的处理器架构做出了不同的选择:

  • 摩托罗拉(PowerPC 等) 使用的是大端序。
  • Intel x86 系列 一直使用小端序。
  • ARM 架构 比较灵活,很多芯片可以配置成大端或小端。

既然大端是符合我们的阅读习惯的,为什么会出现小端呢?主要是为了在某些运算(如整数低位截取)时更方便——因为地址最低的那一位刚好就是最低有效字节。 所以说:大端方便人类阅读,小端方便机器运算

两种字节序具有其各自的优势,但是两种字节序的存在,也导致了新的问题——当字节序不一致时,解析数据将会出错

3. 怎么做

为了避免数据解析出错,我们应该做好两步:

  • 判断当前平台的大小端和待解析数据的大小端是否一致;
  • 如果不一致,则进行转换;

所以,首要任务是判断当前平台是大端还是小端?

3.1 大小端判断

结合大小端的定义——大端的高位字节在低地址处,小端的低位字节在低地址处,我们可以通过一个简单的 C 语言代码来判断当前平台的大小端:

代码语言:javascript代码运行次数:0运行复制
int IsLittleEndian() {
    int a = 1;
    return *(char*)&a == 1;  // 如果最低地址存的是 1,就是小端
}

3.2 数据转换

如果当前平台的大小端和数据的大小端不一致,则需要进行转换。转换的方式有很多:

自己写转换函数

自己书写转换方法也很简单,需要配合移位操作按位与操作。如下是一个简单的转换函数:

代码语言:javascript代码运行次数:0运行复制
uint32_t SwapEndian(uint32_t val) {
    return ((val >> 24) & 0x000000FF) |
           ((val >> 8)  & 0x0000FF00) |
           ((val << 8)  & 0x00FF0000) |
           ((val << 24) & 0xFF000000);
}
借助成熟的序列化库

当前存在很多成熟的序列化库,如protobuf、flatbuffers等,它们都提供了跨平台、跨语言的数据序列化和反序列化功能,并且自动处理了大小端的问题。可以自行查找对应的库,本文不再赘述。

4. 总结

其实大小端是一个老生常谈但又非常基础的问题,本文从是什么、为什么、怎么做三个方面进行了分析,希望对你有所帮助。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。原始发表:2025-04-28,如有侵权请联系 cloudcommunity@tencent 删除原理存储实践数据序列化