Big endian and Little endian

1        概述

 

大小端(endian)会同时涉及byte序和bit序。大端指低地址,高byte,高bit;小端指低地址,低byte,低bit。

为方便讨论,下面以IP地址 A.B.C.D 为例,每个byte中bit值用不同的字母来区分。该IP地址在不同 endian 中内存布局如下所示(从左往右,地址由低到高):

 

========================================

             Big endian (host & network)

========================================

 A                    B                     C                     D

abcdefgh          ijklmnop         qrstuvwx         yz123456

 

 

========================================

            Littleendian (host)

========================================

 D                    C                     B                     A

654321zy         xwvutsrq         ponmlkji          hgfedcba

 

 

========================================

            Littleendian (network)

========================================

A                     B                     C                     D

Hgfedcba         ponmlkji          xwvutsrq         654321zy

 

大端系统主机序和网络序完全一致(byte序和bit序),符合人脑逻辑思维。

小端系统的主机序(byte序和bit序)和大端系统的主机序(或网络序)完全相反。

小端系统的网路序中的byte序和大端系统一致,bit序在每个byte范围内和大端系统相反。

 

 

2       字节序转换

 

glibc 提供的主机序和网络序之间的转换函数ntohl& ntohs & htonl & htons 只转换byte序,不转换(一个byte内的)bit序。

 

3       位域的定义

 

GCC对位域定义的解释是按照物理内存由低到高(对应上图即由左至右)。

 

常见的一个byte内的位域定义如下:

 

#if defined(__LITTLE_ENDIAN_BITFIELD)

            __u8    ihl:4,

                        version:4;

#elif defined (__BIG_ENDIAN_BITFIELD)

            __u8    version:4,

                        ihl:4;

#else

#error  "Pleasefix <asm/byteorder.h>"

#endif

 

如果跨域byte,下面貌似合理的定义,只要对照前面的内存布局,就会发现这样的定义是完全错误的:

 

#if defined(__LITTLE_ENDIAN_BITFIELD)

            __u32  b5:5,

                        b15:15,

                        b12:12;

#elif defined (__BIG_ENDIAN_BITFIELD)

            __u32  b12:12,

                        b15:15,

                        b5:5;

#else

#error  "Pleasefix <asm/byteorder.h>"

#endif

 

所以,位域的定义最好在一个byte之内定义,对于跨越byte之间的位域定义很危险,在小端系统下,这样的定义往往和人脑的逻辑思维不一致。GCC为什么不按逻辑顺序来解释位域的定义呢,个人理解是太复杂,几乎不可能实现。

 

4       移位操作

 

GCC对移位操作的解释是逻辑上的。对应上图,右移操作对于大端系统就是向右移,而对于小端系统就是向左移;左移操作反过来就可以了。由于GCC屏蔽了系统之间的差异,所以,本身移位操作不容易出错。但当移位操作和ntoh& hton等一起用的时候,就要清楚的知道当前的操作是针对网络序还是主机序。建议,移位操作最好针对主机序,减少出错几率。如果一定要是网络序,则建议将移位操作限制在一个byte范围之内,byte的定位通过地址的递增和递减来完成。

 

5       强制类型转换

 

强制类型转换,尤其是大转小的时候,在小端系统下要小心了,如下:

 

uint32_t ip = 0x0A0B0C0D;

uint16_t *p = (uint16_t*)&ip;

Printf("0x%x\n",p[0]); //结果是0x0C0D,而不是0x0A0B

 

如果,转换成网络序后,是不是就没问题了?

ip = htonl(ip);

Printf("0x%x\n",p[0]); //结果是0x0B0A,而不是0x0A0B

 

因为对于*p, p[0]等的操作,GCC的解释是按逻辑,而不是物理来处理的。所以对于强制类型转换,建议针对网络序处理,同时用移位操作来配合,如下:

 

uint32_t ip = 0x0A0B0C0D;

uint8_t *p = (uint8_t*)&ip;

Printf("0x%x\n",p[0]<<8 | p[1]); //结果是0x0A0B


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值