Big Endian(大端)和 Little Endian(小端)模式


核心就是:一个是数据字节顺序,一个内存存储地址顺序

为什么会有 大端模式小端模式的区别的,这是由于编码的原因,因此,先大致介绍一下编码:


1、字符表示(编码)

在之前的计算机中,一般都是用 ASCII 码,但是其只能表示 2^7 = 128 个不同的字符。(仅仅只能表示 英文)

随着计算机的发展,计算机要表示不同国家的文字,比如 中文、泰文等等。那么这个时候,ASCII 编码就完全不够用了。

所以,引入了 Unicode编码,其目的就是:把世界上的文字都映射到一套字符空间中

为了表示Unicode字符集,有下面 3 种(确切的说是 5 种)Unicode的编码方式

  1. UTF-8:很常用的一种。其中 8 表示 8bits,也就是用 1 byte 来表示字符。可以兼容 ASCII码;其特点是:
    • 存储效率高
    • 变长;这是因为, 1 byte 最多也只能 2^8 = 256 个字符,明显不够,所以可以多个 UTF-8 来表示一个字符,可组合。所以其 长度可以不是 1 byte,是可以改变的。但这也导致了,在内部访问的时候,由于不知道具体长度,所以很不方便。
    • 无字节序问题字节序问题就是,大小端模式的不同。因为 UTF-8 只有 1 byte,对于地址而言,一个字节就存在一个地址上,那么就不会出现字节序问题。
  2. UTF-16,其又会具体分为:UTF-16BE (big endian)UTF-16LE (little endian)。其特点为:
    • 定长;因为用了 16 bit,也就是 2 byte 来表示,占用字节长度是固定的。方便内部随机访问。
    • 存在字节序问题;这样子对外部而言,也就是其他机器而言(因为不同机器的字节序会不同),那么到其他机器
  3. UTF-32,其又会具体分为:UTF-32BE (big endian)UTF-32LE (little endian)
    • 这个是在 UTF-16 的基础上,又增加了一个字符的字节表示数。即一个字符,用 32 bit 即 4 byte 来表示。

对于不同的编码,当一个文本文件,用 二进制形式打开的时候,最前面的几个字节就可以区分是什么编码

编码错误的根本原因,在于编码方式和解码方式的不统一,也就是说,当一个机器用的是这种编码方式。到这个文件到了另一个机器时,如果另一个机器用的解码方式对不上,就会导致显示乱码

一般的编程语言,没有声明其 编码方式。而 html 会在一开始声明其 编码方式,这样子方便其他机器知道编码方式后,用对应的解码来处理。所以 html 才用于 网页的设计,因为会在不同机器上传输。


2、什么是字节序

字节序问题,就是,当我们一个东西,用多个 byte 表示的时候。

又因为内存地址,一个地址只表示 1 byte。

所以,对于内存地址而言,地址一般是从 低地址到高地址的。

而,对于多个 byte 表示的那个东西,也会有所谓的字节顺序。举个例子,0x12345678,这是由 4 byte 表示的一个数(也可以是一个字符之类的),那么一般其 字节序就是,高到低为:12 34 56 78(十六进制表示,2位就是 8 bit,即 1 byte)

那么对于,字节表示的内容,当存放在 地址的时候,就会有两种顺序,我们假设内存地址都是 低地址到高地址。同样的,还是 0x12345678,此时有两种情况

情况一:

此时,对于 高位字节位置 对应在 低地址。这和我们直接读的时候,很符合(称为 大端模式 – 低地址对应高字节位置

这个读的时候,由于低地址对应高字节,而我们平时都是高字节读起,所以其存储习惯符合我们直接读取:0x 12 34 56 78 = 0x12345678

   低地址                                            高地址

 ----------------------------------------------------------------------------->

   |     12     |      34    |     56      |     78    |

情况二:

此时,对于 高字节位置,对应在高地址。也就是说,低地址 对应 低字节位置,称为 小端模式。这个时候读字节结果的时候,需要从 高字节到低字节,即 高地址到低地址:0x 12 34 56 78 = 0x12345678

   低地址                                            高地址

   ----------------------------------------------------------------------------->

   |     78     |      56    |     34      |     12    |

3、大小端模式的区别

上面也大概介绍了一下 字节序的问题。

一般字节序的问题,是由于系统自身存储的问题,一般分为两大 CPU派系:那就是Motorola的PowerPC系列CPU和Intel的x86系列CPU。PowerPC系列采用big endian方式存储数据,而x86系列则采用little endian方式存储数据。

big endian是指低地址存放最高有效字节(MSB)

little endian则是低地址存放最低有效字节(LSB)

记忆方式:内存地址,永远都是从 低地址开始的
大端模式:低地址 对应 高字节(大 – 高字节)
小端模式:低地址 对应 低字节(小 – 低字节)

举个例子

数字0x12345678在两种不同字节序CPU中的存储顺序如下所示

大端模式

此时,对于 高位字节位置 对应在 低地址。这和我们直接读的时候,很符合(称为 大端模式 – 低地址对应高字节位置

这个读的时候,由于低地址对应高字节,而我们平时都是高字节读起,所以其存储习惯符合我们直接读取:0x 12 34 56 78 = 0x12345678

   低地址                                            高地址

 ----------------------------------------------------------------------------->

   |     12     |      34    |     56      |     78    |

小端模式

此时,对于 高字节位置,对应在高地址。也就是说,低地址 对应 低字节位置,称为 小端模式。这个时候读字节结果的时候,需要从 高字节到低字节,即 高地址到低地址:0x 12 34 56 78 = 0x12345678

   低地址                                            高地址

   ----------------------------------------------------------------------------->

   |     78     |      56    |     34      |     12    |

C/C++ 语言编写的程序里数据存储顺序是跟编译平台所在的CPU相关的,而JAVA编写的程序则唯一采用big endian方式来存储数据。
试想,如果你用C/C++ 语言在x86平台下编写的程序跟别人的JAVA程序互通时会产生什么结果?就拿上面的0x12345678来说,你的程序传递给别人的一个数据,将指向0x12345678的指针传给了JAVA程序,由于JAVA采取big endian方式存储数据,很自然的它会将你的数据翻译为0x78563412。因此,在你的C程序传给JAVA程序之前有必要进行字节序的转换工作。

所有网络协议也都是采用big endian的方式来传输数据的。所以有时我们也会把big endian方式称之为网络字节序。当两台采用不同字节序的主机通信时,在发送数据之前都必须经过字节序的转换成为网络字节序后再进行传输。

如何 C 编程判断CPU是大端还是小端模式

// 若处理器是Big_endian的,则返回0;若是Little_endian的,则返回1。
// 解释:union 是共用体,即里面的所有数据是共用同一块内存空间的,由占据字节最大的绝对
// 这个里面有 int 和 char,int 是 4 字节,char 是 1 字节。所以 union w 总共占据 4 字节的内存
// 我们将 c.a = 1,那么根据 int 的,那么 a 的值为 0x 00 00 00 01
// 假设顺序是 低地址到高地址
// 如果是 00 00 00 01 的存,那就是,低地址对应高字节,大端模式,那么此时对于 char b 而言,只占据 1 字节,所以 b = 0x00,所以 b = 0 为 大端模式
// 如果是 01 00 00 00 的存,那就是,低地址对应低字节,小端模式,那么此时对于 b = 0x01,即 b = 1 为小端模式
// 所以 return (c.b == 1); 当 返回 1(真),就是小端模式;返回 0,为 大端模式

int checkCPU()
{
 {
  union w
  {
   int a;
   char b;
  } c;
  c.a = 1;
  return (c.b == 1);
 }
}

例题一

请问下列代码的输出结果有可能是哪些()?

#include<stdint.h>
#include<stdio.h>
union X
{
    int32_t a;
    struct 
    {
        int16_t b;
        int16_t c;
    };
};
int main(){
    X x;
    x.a=0x20150810;
    printf("%x,%x\n",x.b,x.c);
    return 0;
}

A. 2015,810
B. 50810,201
C. 810,2015
D. 20150,810

解析

首先先根据 union 的定义,是由其中最大的字节占用据欸的那个 union 占用的内存空间

union X
{
    int32_t a; // 那就是 32 bit,也就是 4 字节
    // struct 是里面的字节占用空间之和(还有其偏移),这个总共占据 4 字节
    struct 
    {
        int16_t b;
        int16_t c;
    };
};
// 所以 union 总共占据 4 字节

那么当 a = 0x20150810,根据大端和小端的情况,有两种可能:

当是大端时,假设都是从低地址到高地址,那么此时是,低地址对应高字节,那么 20 15 08 10,而 b 和 c 来分这个 4 字节,一人两个字节,那么此时 b 为 20 15,注意此时是 低地址对高字节,所以 b = 0x2015;同理 c = 0x0810

当是小端时,低地址对应低字节,那么此时 a 存储在 内存地址上的为 10 08 15 20,那么分配给 b 和 c,此时 b 为 10 08,而低地址对应低字节

答案

A、C

例题二:大端模式向小端模式发送数据

假设 A 给 B 发送数据,其中 A 是 大端模式,B是 小端模式

此时,A中的数据是 0x12345678

因为 A 是 大端模式,那么就是,低地址对应高字节,即 12 34 56 78,此时,发送给 B(从 A 发给 B,从低地址发,B 也从低地址收),那么 B 接收到的为 12 34 56 78

但是,B 是小端模式,即 低地址对应低字节,那么 B 收到的数据为 0x78563412


4、字节序转换函数

主机字节序一般都是小端(绝大多数,少部分也是大端存储的),网络字节序是大端存储的。

因此,对于从 主机中,发送给另一个主机,一般先将该主机的变为 网络字节序,然后另一个主机接收到 网络字节序后,再转换为 自己的字节序

#include <arpa/inet.h>

1.htons():把unsigned short类型从主机序转换到网络序(h:host,主机;n:net,网络;s:unsigned short16位短整数)
2.htonl():把unsigned long类型从主机序转换到网络序(l:unsigned long32位长整数)
3.ntohs():把unsigned short类型从网络序转换到主机序
4.ntohl():把unsigned long类型从网络序转换到主机序

如果主机序是小端字节序,这些函数将参数做相应的大小端转换然后返回,如果主机序是大端字节序,这些函数将不对参数做转换,将参数原封不动地返回。

5.uint32_t htonl(uint32_t hostint32);
功能:将32位主机字节序数据转换为网络字节序数据
参数:hostint32,需要转换的32位主机字节序数据,uint32_t32位无符号整型
返回值:若成功,返回网络字节序的值

6.uint16_t htons(uint16_t hostint16);
功能:将16位主机字节序数据转换成网络字节序数据
参数:hostint16,需要转换的16位主机字节序数据,uint16_t16为无符号短整型
返回值:若成功,返回网络字节序的值

7.uint32_t ntohl(uint32_t netint32);
功能:将32位网络字节序数据转换为主机字节序数据
参数:netint32,需要转换的32位网络字节序数据,uint32_t32位无符号整型
返回值:若成功,返回主机字节序的值

8.uint16_t ntohs(uint16_t netint16);
功能:将16位网络字节序数据转换成主机字节序数据
参数:netint16,需要转换的16位网络字节序数据,uint16_t16为无符号短整型
返回值:若成功,返回主机字节序的值
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值