大端模式和小端模式初探

大端模式和小端模式初探

字节序模式由来

关于大端小端名词的由来,有一个有趣的故事,来自于Jonathan Swift的《格利佛游记》:Lilliput和Blefuscu这两个强国在过去的36个月中一直在苦战。战争的原因:大家都知道,吃鸡蛋的时候,原始的方法是打破鸡蛋较大的一端,可是那时的皇帝的祖父由于小时侯吃鸡蛋,按这种方法把手指弄破了,因此他的父亲,就下令,命令所有的子民吃鸡蛋的时候,必须先打破鸡蛋较小的一端,违令者重罚。然后老百姓对此法令极为反感,期间发生了多次叛乱,其中一个皇帝因此送命,另一个丢了王位,产生叛乱的原因就是另一个国家Blefuscu的国王大臣煽动起来的,叛乱平息后,就逃到这个帝国避难。据估计,先后几次有11000余人情愿死也不肯去打破鸡蛋较小的端吃鸡蛋。这个其实讽刺当时英国和法国之间持续的冲突。Danny Cohen一位网络协议的开创者,第一次使用这两个术语指代字节顺序,后来就被大家广泛接受。

什么是大端和小端

大端模式(Big-Endian)

高位字节放置在内存的低地址端,低位字节放置在内存的高地址端

比如数字0x12345678(12是数字的高位字节,78是数字的低位字节),大端模式的表示形式为

低地址 >>>>>>>>>>>>>>>>> 高地址

0x12 | 0x34 | 0x56 | 0x78

小端模式(Little-Endian)

低位字节放置在内存的低地址端,高位字节放置在内存的高地址端

比如上述数字0x12345678, 小端模式的表示形式为

低地址 >>>>>>>>>>>>>>>>> 高地址

0x78 | 0x56 | 0x34 | 0x12

示例

16bit宽的数0x1234在Little-endian模式(以及Big-endian模式)CPU内存中的存放方式(假设从地址0x4000开始存放)为:

内存地址 小端模式存放内容 大端模式存放内容
0x4000 0x34 0x12
0x4001 0x12 0x34

32bit宽的数0x12345678在Little-endian模式以及Big-endian模式)CPU内存中的存放方式(假设从地址0x4000开始存放)为:

内存地址 小端模式存放内容 大端模式存放内容
0x4000 0x78 0x12
0x4001 0x56 0x34
0x4002 0x34 0x56
0x4003 0x12 0x78

优缺点

大端模式(Big-Endian):符号位在所表示的数据的内存的第一个字节中,便于快速判断数据的正负和大小

小端模式(Little-Endian):

  • 内存的低地址处存放低字节,所以在强制转换数据时不需要调整字节的内容(注解:比如把int的4字节强制转换成short的2字节时,就直接把int数据存储的前两个字节给short就行,因为其前两个字节刚好就是最低的两个字节,符合转换逻辑)
  • CPU做数值运算时从内存中依顺序依次从低位到高位取数据进行运算,直到最后刷新最高位的符号位,这样的运算方式会更高效

为什么会有大小端之分呢

计算机系统中内存是以字节为单位进行编址的,每个地址单元都唯一的对应着一个字节(8 bit)。这可以应对char数据类型的存储要求,因为char类型刚好1个字节,但是有些类型的长度是多个字节的,比如2字节的short4字节的int等。因此这里就出现了如何安排多个字节数据中各字节存放顺序的问题,是高位字节放在低地址段呢?还是低位字节放在低地址段呢?正是不同的防止顺序导致了大端模式和小端模式的出现。

如何判断机器的字节序

既然已经了接了大端模式和小端模式只是两种不同存储字节的方式,那么如果通过程序判断机器的字节序呢,下面程序中给出两种方式判断电脑的字节序:

/*
 * 方法一
 * 通过将int类型强制转换为char类型,所以单字节的char类型变量取得了4字节的int类型变量的低地址部分,然后 
 * 通过判断低地址部分的值获取字节序。
*/
bool is_big_endian_1()
{
    int a = 0x1234;
    printf("%zd\n", sizeof(a));
    printf("%x\n", a);
    char b;
    b = *(char *)&a;
    if(0x12 == b)
        return true;
    return false;
}

/*
 * 方法二
 * 联合体union的存放顺序是所有成员都从低地址开始存放,然后获取低地址部分来判断机器字节序 
 */
bool is_big_endian_2()
{
    union NUM
    {
        int a;
        char b;
    } num;

    num.a = 0x1234;
    if(0x12 == num.b)
        return true;
    return false;
}

现状

一般操作系统都是小端,而通讯协议是大端的。

常见CPU的字节序

  • Big Endian : PowerPC、IBM、Sun
  • Little Endian : x86、DEC
  • ARM既可以工作在大端模式,也可以工作在小端模式。

常见文件的字节序

  • Adobe PS – Big Endian
  • BMP – Little Endian
  • DXF(AutoCAD) – Variable
  • GIF – Little Endian
  • JPEG – Big Endian
  • MacPaint – Big Endian
  • RTF – Little Endian

注意 :Java和所有的网络通讯协议都是使用Big-Endian的编码。

主机字节序和网络字节序

主机字节序

不同的CPU有不同的字节序,这些字节序决定了数据在内存中的保存顺序,即主机字节序。主机字节序最常见的有两种,即上面提到的:大端模式和小端模式。

网络字节序

网络字节序是TCP/IP中规定的一种数据表示格式,它与具体的CPU类型、操作系统无关,从而可以保证数据在不同主机之间传输时能被正确解释。网络字节序采取大端模式。

Linux下进行网络编程时,经常用到htonshtonl两个函数,他们就是将主机字节序转换成网络字节序。

字节序转换

大小端转换

// 实现16bit的数据之间的大小端转换
#define BLSWITCH16(A)   (  ( ( (uint16)(A) & 0xff00 ) >> 8  )    | \  
                           ( ( (uint16)(A) & 0x00ff ) << 8  )     )  

// 实现32bit的数据之间的大小端转换
#define BLSWITCH32(A)   (  ( ( (uint32)(A) & 0xff000000) >> 24) |\
         (((uint32)(A) & 0x00ff0000) >> 8) | \
         (((unit32)(A) & 0x0000ff00) << 8) | \
         (((uint32)(A) & 0x000000ff) << 32)  )

网络字节序和主机字节序转换

由于网络字节序一律为大端,而目前个人PC大部分都是X86的小端模式,因此网络编程中不可避免得要进行网络字节序和主机字节序之间的相互转换,下面是 socket 提供的转换函数

#define ntohs(n)     // 16位数据类型网络字节顺序到主机字节顺序的转换  
#define htons(n)     // 16位数据类型主机字节顺序到网络字节顺序的转换  
#define ntohl(n)     // 32位数据类型网络字节顺序到主机字节顺序的转换  
#define htonl(n)     // 32位数据类型主机字节顺序到网络字节顺序的转换

参考

猜你喜欢

转载自blog.csdn.net/u011221820/article/details/79903809
今日推荐