字节序与大小端含义及判断方法

字节的高低位

通常我们从最高有效位(most significant digit)开始自左向右书写一个数字。在理解有效位这个概念时,可以想象一下你的支票数额的第一位增加1和最后一位增加1之间的巨大区别,前者肯定会让你喜出望外。

计算机内存中一个字节的位相当于二进制数的位,这意味着最低有效位表示1,倒数第二个有效位表示2×1或2,倒数第三个有效位表示2×2×1或4,依此类推。如果用内存中的两个字节表示一个16位的数,那么其中的一个字节将存放最低的8位有效位,而另一个字节将存放最高的8位有效位,如下图。

存放最低的8位有效位的字节被称为最低有效位字节或低位字节,而存放最高的8位有效位的字节被称为最高有效位字节或高位字节。

-------- 高位字节 ------------- 低位字节--------
↓------------------------↓ ↓--------------------------↓

15 14 13 12 11 10 9. 8. 7. 6. 5. 4. 3. 2. 1. 0.

字节序与计算机存储大小端

由于现代设备的多样性,不同设备对于数据内容的存储格式有所差异,而目前对于数据的划分通常以字节为单位,由此引申出了字节序的概念,即:不同主机自有的主机字节序和在主机与主机之间通道传播过程中的网络字节序

目前主机字节序主要由主机字节内部的存储顺序决定为两种:

  • 大端模式(big-endian):
    是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址

  • 小端模式(little-endian):
    是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址

比如如果我们要将一个值为0x123456的数据存储在计算机地址在0x4000 8000~0x4000 8002范围内的内存中,根据不同的存储大小端可以得到以下结果:

计算机地址0x4000 80000x4000 80010x4000 8002
小端模式0x560x340x12
大端模式0x120x340x56

需要说明的是:如果此时有一个指针指向这一个数据,那么不论计算机存储模式是大端还是小端,这个指针的值始终为0x4000 8000,即存储这个数据空间的最低位地址

目前在网络上传输的网络字节序都通常默认为大端模式存储的字节顺序

大小端判断

对于计算机大小端的判断方法目前主流的有以下两种:

1. 通过指针类型的转换

由于如在32位操作系统中,char型变量在存储空间中占1字节,而int型变量在存储空间中占4字节,由此可以通过一个指向int型变量的指针强制转换成一个指向char型变量的指针,由于指针存储的仅是指向对象位置的地址,因此计算机在转换指针类型时并不会改变指针的值,而改变了在使用此指针间接读取指向对象的值时的读取方式,例如int型将读取4个字节,而char型仅读取一个字节。

通过这种操作便可以把int型变量在内存中首地址的第一个字节单元类容提取出来了。

代码如下:

//判断大小端,返回1为大端,返回-1为小端
int CheckEndian_1(){
    int a = 0x123456;
    int * pa = &a;
    if(*((char *) pa) == 0x12)  // big endian
        return 1;
    if(*((char *) pa) == 0x56)  // little endian
        return -1;  
    return 0;
}

2. 通过union的特性判断

在C语言中,不同于结构体,共用体(联合体,即union)中的几种不同类型的变量存放在同一段内存单元中,其数据成员都是从低地址开始存放的,通过对后续成员进行访问便能获取前一个成员的尾部字节的内容,如下:

//判断大小端,返回1为大端,返回-1为小端
int CheckEndian_2(){
  union w{
      int a;  //4 bytes
      char b; //1 byte
  } c;
  c.a = 0x123456;
  if (c.b == 0x12)  // little endian
      return -1;
  if (c.b == 0x56)  // big endian
      return 1;
  return 0;
}

字节序的转换

主机在向网络发送/接收字节信息时,通常要将字节序进行一个转换,如函数ntohl(),其表示network to host (unsigned) long,即将long(32位)网络字节序转换成适应的主机字节序,其转换函数的代码也比较易于理解,如下:

#if _BYTE_ORDER == _BIG_ENDIAN  // big endian

#define ntohl(x)        (x)
#define ntohs(x)        (x)
#define htonl(x)        (x)
#define htons(x)        (x)

#else  // little endian

#define ntohl(x)        ((((x) & 0x000000ff) << 24) | \
                         (((x) & 0x0000ff00) <<  8) | \
                         (((x) & 0x00ff0000) >>  8) | \
                         (((x) & 0xff000000) >> 24))

#define htonl(x)        ((((x) & 0x000000ff) << 24) | \
                         (((x) & 0x0000ff00) <<  8) | \
                         (((x) & 0x00ff0000) >>  8) | \
                         (((x) & 0xff000000) >> 24))

#define ntohs(x)        ((((x) & 0x00ff) << 8) | \
                         (((x) & 0xff00) >> 8))

#define htons(x)        ((((x) & 0x00ff) << 8) | \
                         (((x) & 0xff00) >> 8))

#endif /* _BYTE_ORDER */

参考文章:
高位字节与低位字节
用union判断cpu的大小端
网络字节序和主机字节序详解
大小端
ntohl()、htonl()函数
使用Union判断大小端

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值