大小端模式

12/7/2021 c/c++

我们从小就知道,数字是从左往右读的,这符合人类的普遍阅读习惯。 比如十进制数65535,数位从高到低依次是万,千,百,十,个。对十进制数而言,最左边是高位数位,最右边是低位数位。

我们类比到 16 进制数0x12345678,也从左往右来阅读这个数。那么,对十六进制数而言,最左边就是高位字节,最右边是低位字节

端模式(Endian)的这个词出自 Jonathan Swift 书写的《格列佛游记》。这本书根据将鸡蛋敲开的方法不同将所有的人分为两类,从圆头开始将鸡蛋敲开的人被归为 Big Endian,从尖头开始将鸡蛋敲开的人被归为 Littile Endian(这句话最为形象)。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开。在计算机业 Big Endian 和 Little Endian 也几乎引起一场战争。在计算机业界,Endian 表示数据在存储器中的存放顺序。下文举例说明在计算机中大小端模式的区别。

如果将一个 32 位的整数0x12345678存放到一个整型变量(int) 中,这个整型变量采用大端或者小端模式在内存中的存储由下表所示。

大端 小端
  • 小端:较高的有效字节存放在较高的的存储器地址,较低的有效字节存放在较低的存储器地址。
  • 大端:较高的有效字节存放在较低的存储器地址,较低的有效字节存放在较高的存储器地址

采用大小模式对数据进行存放的主要区别在于在存放的字节顺序,大端方式将高位存放在低地址,小端方式将高位存放在高地址。

到目前为止,采用大端或者小端进行数据存放,其孰优孰劣也没有定论:

  • 小端模式:强制转换数据不需要调整字节内容,1、2、4 字节的存储方式一样。
  • 大端模式:符号位的判定固定为第一个字节,容易判断正负。

# 为什么会有大小端模式之分呢?

这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为 8bit。但是在 C 语言中除了 8bit 的 char 之外,还有 16bit 的 short 型,32bit 的 long 型(要看具体的编译器),另外,对于位数大于 8 位的处理器,例如 16 位或者 32 位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。

例如一个 16bit 的 short 型 x,在内存中的地址为 0x0010,x 的值为 0x1122,那么 0x11 为高字节,0x22 为低字节。对于大端模式,就将 0x11 放在低地址中,即 0x0010 中,0x22 放在高地址中,即 0x0011 中。小端模式,刚好相反。我们常用的 x86 结构是小端模式,而 KEIL C51 则为大端模式。很多的 ARM,DSP 都为小端模式。有些 ARM 处理器还可以由硬件来选择是大端模式还是小端模式。

# 判断大小端

# 使用联合体 union:

//return 1 : little-endian
//       0 : big-endian
int little_or_big()
{
    union {
        unsigned int a;
        unsigned char b;
    } c;

    c.a = 1;
    return c.b;
}

首先共用体元素 a 和 b 在访问时候都是从低地址开始访问的,c.a = 1 在内存中的存放有两种可能(内存地址从左到右递减),小端模式为 00 00 00 01;大端模式为: 01 00 00 00 ,而共用体 c 中的 b 是 char 类似,所以我们用 c.b 去访问时只能读取到最低地址的值(按 char 去解析时只会读取一个字节),所以,如果读出 c.b 的值为 1 则说明当前机器是小端模式,读出 c.b 的值为 0, 则说明当前机器是大端模式

# 指针方式来测试大小端

int little_or_big2(void)
{
    int a = 1;
    char b = *((char *) &a);
    return b;
}

首选定义变量 a= 1,然后将 a 的指针强制类型转换成 char * 接着去解应用这个指针,并赋值给 b,然后根据 b 的返回值来确定大小端。其实分析可以发现其本质都是一样的,都是先给一个内存里面存一个 char 类型的 1,然后使得另一个 char 类型的变量 b 去读取这个内存的值,然后根据读取的值来判断大小端。

上次更新: 5/5/2022, 8:45:22 AM