字节序详解


1. 首先建立如下的物理内存概念(独立于字节序)


如下面的图-1所示,内存中有连续的4个字节,左边是低地址,右边是高地址。

我们这里假设4个字节的地址分别是0,1,2,3。



低地址                                                      高地址

|<--------------------------bits-------------------------------->
0                                                             31

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |               |               |               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

        0               1               2               3      
|<--------------------------bytes------------------------------->


                            图-1


接下来,我们要进一步引入bit地址的概念。

我们认为,每个bit也有地址。并且,与字节的地址顺序一样,左边是低地址,右边是高地址。

这样一来,左边是低地址的字节,右边是高地址的字节。

在每一个字节内部呢,左边是低地址的bit,右边是高地址的bit。

好了,现在我们建立了这样的物理内存概念。

注意,这个物理内存概念是独立与字节序的,并且是独立与cpu类型、内存类型的。

也就是说,不管你是什么系统,这个概念都是一样的。


2.  字节序的概念

假设有一个整数,占据了上图中的一片连续的bit位。那么这个整数的数值是多少呢?

这就取决于cpu的字节序了。

大字节序机器:低地址的bit是高有效位,高地址的bit是低有效位

小字节序机器:低地址的bit是低有效位,高地址的bit是高有效位



3. 存储空间的分配(独立于字节序)

typedef struct

{

    char a;

    char b1:3;

    char b2:5;

    uint16_t  c1:6;

    uint16_t  c2:10;

} t_my_type;


上述C语言定义的结构体中,各成员所占的内存空间是如何分配的呢?

答案是,按成员定义的顺序,从低地址向高地址依次分配空间。

注意,这种分配方式是独立与cpu类型、内存类型的。

也就是说,不管你是什么系统,分配方式都是如此。

按照上述分配方式,上述结构的各成员在内存中的分布如下面图-2所示:


低地址                                                      高地址
|<--------------------------bits-------------------------------->
0                                                             31


+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|       a       |  b1 |  b2     |     c1    |    c2             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


        0               1               2               3      
|<--------------------------bytes------------------------------->


                            图-2


4.  数据的网络发送

按照字节地址从低到高的顺序发送:低地址的字节在前,高地址的字节在后。

如果再细化到bit的话,则同一个字节内:高有效位的bit在前,低有效位的bit在后。

这样一来,对于每一个字节内的bit位的发送顺序,大小字节序的机器就有如下区别了:

大字节序机器:按地址由低到高的顺序发送bit位

小字节序机器:按地址由高到低的顺序发送bit位


5.  数据的网络接收

先到达的字节为低地址字节,后到达的字节为高地址字节。

同样,如果再细化到bit的话,则同一个字节内:高有效位的bit先到达,低有效位的bit后到达。

这样一来,对于每一个字节内的bit位的存储位置,大小字节序的机器就有如下区别了:

大字节序机器:按bit到达顺序从低地址向高地址存储

小字节序机器:按bit到达顺序从高地址向低地址存储


6. 应用

由于gcc工具链自然知道自己是为什么cpu编译代码,因此工具链自身是包含了目标cpu的字节序信息的。

对于程序员而言,只要包含endian.h,然后通过检查__BYTE_ORDER宏的值,就能知道当前是为什么字节序的cpu生成代码。

下面举个小例子,说明主机间通过网络通信时,如何正确的处理字节序问题。

假设两台主机(字节序可能相同,也可能不同),需要通过网络传递一个结构化的信息,信息由a,b,c,d这4个整数构成。

那么,此结构可以定义成如下形式。

#include <stdint.h>
#include <endian.h>
typedef struct
{
    uint16_t   a;
    uint32_t   b;
#if __BYTE_ORDER == __BIG_ENDIAN
    uint16_t  c:3;
    uint16_t  d:13;
#else  if  __BYTE_ORDER == __LITTLE_ENDIAN
    uint16_t  d:13;
    uint16_t  c:3;
#else
#error  can not get byte order info
#endif
} __attribute__((packed)) t_msg;



发送消息时,按如下方式封装此结构(不管封装机器字节序如何)

#include <arpa/inet.h>
t_msg   the_msg;
uint16_t *p16=(void *)&the_msg + sizeof(the_msg.a) + sizeof(the_msg.b);   //p16指向成员b后面的内容
the_msg.a = htons(1234);
the_msg.b = htonl(1234);
the_msg.c = 5;
the_msg.d = 8;
*p16 = htons(*p16);
//封装完毕,可以发送了



收到消息时,按如下方式取出此结构中的数值(不管封装机器字节序如何)

#include <arpa/inet.h>
t_msg   the_msg;
//接收消息到the_msg变量中。
uint16_t *p16=(void *)&the_msg + sizeof(the_msg.a) + sizeof(the_msg.b);   //p16指向成员b后面的内容
the_msg.a  = ntohs(the_msg.a);
the_msg.b  = ntohl(the_msg.b);
*p16 = ntohs(*p16);
//好了,现在the_msg中的成员,都变成主机序了,可以直接使用了


最后,再看看如下Linux源码中tcp头的定义,再结合字节序转换函数的功能作用,对于字节序您是否有所领悟呢:)

struct tcphdr {
__be16 source;
__be16 dest;
__be32 seq;
__be32 ack_seq;
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u16 res1:4,
doff:4,
fin:1,
syn:1,
rst:1,
psh:1,
ack:1,
urg:1,
ece:1,
cwr:1;
#elif defined(__BIG_ENDIAN_BITFIELD)
__u16 doff:4,
res1:4,
cwr:1,
ece:1,
urg:1,
ack:1,
psh:1,
rst:1,
syn:1,
fin:1;
#else
#error "Adjust your <asm/byteorder.h> defines"
#endif
__be16 window;
__sum16 check;
__be16 urg_ptr;
};


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值