字节序(大小端)

什么是字节序

字节序,简单来说,就是字节的顺序,指的超过一个字节的数据类型在内存中存储的顺序(一个字节显然不需要顺序)。
字节序在不同的主机平台上分为大端字节序、小端字节序。

小端字节序

高位字节数据存放在高地址处,低位数据存放在低地址处。linux和windows平台一般都采用小端字节序。
采用union可以打印出内存中的字节顺序:

void show_little_endian()
{
    union{
        char byte[4];
        int num;
    }test_num;

    test_num.num = 0x12345678;

    printf("[0]:%p:0x%X\n"
            "[1]:%p:0x%X\n"
            "[2]:%p:0x%X\n"
            "[3]:%p:0x%X\n",
            &test_num.byte[0], test_num.byte[0],
            &test_num.byte[1], test_num.byte[1],
            &test_num.byte[2], test_num.byte[2],
            &test_num.byte[3], test_num.byte[3]);
    /*
    [0]:0x7ffdeb35bb94:0x78
    [1]:0x7ffdeb35bb95:0x56
    [2]:0x7ffdeb35bb96:0x34
    [3]:0x7ffdeb35bb97:0x12*/
}

小端字节序

如上所示,低字节0x7ffdeb35bb94存放的是地位0x78,高字节0x7ffdeb35bb97存放的是高位0x12。

大端字节序

大端就是将高位字节放到内存的低地址端,低位字节放到高地址端。一般网络上都使用大端字节序进行二进制数据的传输。

void show_big_endian()
{
    union{
        char byte[4];
        int num;
    }test_num;

    test_num.num = htonl(0x12345678);

    printf("[0]:%p:0x%X\n"
            "[1]:%p:0x%X\n"
            "[2]:%p:0x%X\n"
            "[3]:%p:0x%X\n",
            &test_num.byte[0], test_num.byte[0],
            &test_num.byte[1], test_num.byte[1],
            &test_num.byte[2], test_num.byte[2],
            &test_num.byte[3], test_num.byte[3]);
}
    /*
    [0]:0x7ffd7f6abbd4:0x12
    [1]:0x7ffd7f6abbd5:0x34
    [2]:0x7ffd7f6abbd6:0x56
    [3]:0x7ffd7f6abbd7:0x78
    */

大端字节序

如上所示,低字节0x7ffd7f6abbd4存放的是低位0x12,高字节0x7ffd7f6abbd7存放的是高位0x78。

主机字节序

指运行程序的主机的字节序,可以采用下面方式获取主机的字节序:

bool isLittlEndian()
{
    uint32_t n1 = 1;
    uint32_t n2 = htonl(n1);
    return n1 != n2; // 如果htonl生效(n2!=n1),则说明主机是小端,n2则是按大端字节序排序的
}

bool isLittlEndian2()
{
    union{
        char byte[4];
        int num;
    }test_num;
    test_num.num = 0x01u;
    return test_num.byte[0] == 0x01u; // 通过低字节是地位来判断
}

为什么要有大小端

小端字节序的优势:计算机电路先处理低位字节,采用小端字节序效率比较高。
大端字节序的优势:数据的储存方式和人的阅读方式一致,更利于直接阅读。

大小端的转换

将对应的字节顺序颠倒即可
大小端的转换

#define BigLittleSwap16(A)        ((((uint16_t)(A) & 0xff00) >> 8) | \
                                                       (((uint16_t)(A) & 0x00ff) << 8))


#define BigLittleSwap32(A)        ((((uint32_t)(A) & 0xff000000) >> 24) | \
                                                       (((uint32_t)(A) & 0x00ff0000) >> 8) | \
                                                       (((uint32_t)(A) & 0x0000ff00) << 8) | \
                                                       (((uint32_t)(A) & 0x000000ff) << 24))

#define BigLittleSwap64(A)        ((uint64_t)BigLittleSwap32((A & 0xFFFFFFFF)) << 32 |  \
                                                       (uint64_t)BigLittleSwap32((A >> 32)))

double、float这类浮点数也需要进行字节序转换,转换方法参考同大小的int

常见类型的主机字节序和网络字节序转换

Linux 和 windows 都提供了接口对数据进行转换

uint32_t htonl(uint32_t hostlong);    // 32位数据(int,int32_t,unsigned int,uint32_t,float)主机序转网络字节序
uint16_t htons(uint16_t hostshort);    // 16位数据(short,int16_t,unsigned short,uint32_t,float)主机序转网络字节序
uint32_t ntohl(uint32_t netlong);    // 32位数据(int,int32_t,unsigned int,uint32_t,float)网络字节序转主机序
uint16_t ntohs(uint16_t netshort);    // 16位数据(int,int32_t,unsigned int,uint32_t,float)网络字节序转主机序    

int64_t,uint64_t,double可以参考BigLittleSwap64
需要注意的是,windows和linux的long类型定义不同,windows定义的是32位,而linux定义的是64位,网络传输时会有歧义,应该避免使用

结构体的大小端转换

结构体内的每个元素都是独立的,需要对每个元素进行大小端转换。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值