数据大小端问题

大小端

前言


在讨论大小端前我们先要了解: memset 和 memcpy 函数,它们的最小单位是以字节(byte)为单位。


函数引用(接下来使用此函数对高低位进行切换)

void MainWindow::reverseArray(char *src, char *dest, uint length)
{
    for(uint i=0; i<length/4; i++)
    {
        dest[4*i+0] = src[4*i+3];
        dest[4*i+1] = src[4*i+2];
        dest[4*i+2] = src[4*i+1];
        dest[4*i+3] = src[4*i];
    }
}

先看截取的对于大小端的定义

请添加图片描述

大小端数据的直观理解[重点]

  * 为什么要区分 大小端字节序
 *:0x12345678(此种写法 左侧高字节 右侧低字节)
 * 地址显示: 0000 0001 0002 0003
 * 小端显示: 78   56   34   12
 * 大端显示: 12   34   56   78
 * 我们知道当我们 memset或memcpy从内存读数时都是从低地址往出读的
 * 注意:我们读出来数据时是从低位读的,
 * 那么小端模式下(读出来的低地址数据在低位):读出来的数写到 目的地址的时候也是依次向右挤的(头插法)
 * 在大端模式下(读出来的低地址数据在高位):读出来的数写到 目的地址的时候是依次向右添加的(尾插法)
 * 此时当我们memset 1字节时读出来的是:
 * 小端读小端:0x 78
 * 大端读大端:0x 12
 * 小端机器上读大端数据0x 12
 *  * 此时当我们memset 2字节时读出来的是:
 * 小端读小端:0x 56 78
 * 大端读大端:0x 12 34
 * 小端机器上读大端数据:0x 34 12
 *  * 此时当我们memset 3字节时读出来的是:
 * 小端读小端:0x 34 56 78
 * 大端读大端:0x 12 34 56
 * 小端机器上读大端数据:0x 56 34 12
 *  * 此时当我们memset 4字节时读出来的是:
 * 小端读小端:0x 12 34 56 78
 * 大端读大端:0x 12 34 56 78
 * 小端机器上读大端数据:0x 78 56 34 12
 * */

对uchar数组进行取操作

定义一个uint8_t的数组观察起内部内存

char _32low[] ={ 0x12,0x34,0x56,0x78,0x90,0x11,0x12,0x13};
从下方图片可以看到数组[0]的地址比数组[3]的地址低

请添加图片描述
请添加图片描述

尝试使用uint32_t 每次区 1、2、3、4byte数据

	char _32low[] ={ 0x12,0x34,0x56,0x78,0x90,0x11,0x12,0x13};//小端
	char abca[8];
	
    reverseArray((char*)&_32low,abca,8);//这里模拟讲小端转为大端 变为{0x78,0x56,0x34,0x12,0x13,0x12,0x11,0x90}
    //注意数组左侧为低地址、右侧为高地址
    memcpy(&_32high1,abca+0,1);//由于我们是小端机器,那么memcpy后变成 0x78
    //注意0x56是高地址,所以在0x78的前面
    memcpy(&_32high2,abca+0,2);//由于我们是小端机器,那么memcpy后变成 0x5678
    memcpy(&_32high3,abca+0,3);//由于我们是小端机器,那么memcpy后变成 0x345678
    memcpy(&_32high4,abca+0,4);//由于我们是小端机器,那么memcpy后变成 0x12345678
	
	uint8_t _81;
    uint16_t _161;
    uint32_t _321;
    memcpy(&_81,abca+2,1);//由于我们是小端机器,那么memcpy后变成 0x34
    memcpy(&_161,abca+2,2);//由于我们是小端机器,那么memcpy后变成 0x1234
    memcpy(&_321,abca+2,4);//由于我们是小端机器,那么memcpy后变成 0x12131234



提升


经过上面的介绍一定对大小端有了一定的了解,那么接下来进行的是 memcpy数组到另一个数组

uchar[]给uchar[]

char _32low[] ={ 0x12,0x34,0x56,0x78,0x90,0x11,0x12,0x13};//小端
char abca[8];

memcpy(abca,_32low,8);
//abca得到的是[0]=0x12 [1]=0x34 。。。原因是从低地址memcpy到高地址

uint32_t[]给uint32_t[]

#pragma pack(1)
    uint32_t u32[8]={0x12345678,0x90111213,0x14151617,0x18192021,0x22232425,0x26272829,0x30313233,0x34353637};
#pragma pack(0)
    uint32_t u32new[8];
    memcpy(u32new,u32,sizeof(uint32_t)*8);
    //分析一下 
    //地址 1000 1001 1002 1003
    //数据 0x13 0x12 0x11 0x90
    //memcpy时我们是小端的机器所以从小端
    memcpy(&_321,u32new+1,4);//因为我们小端设备,所以memcpy时会从低地址读写到我们的低地址 所以写出来是0x90111213

uint32_t[]给uint8_t[]

#pragma pack(1)
    uint32_t u32[8]={0x12345678,0x90111213,0x14151617,0x18192021,0x22232425,0x26272829,0x30313233,0x34353637};
#pragma pack(0)
    memcpy(abca,u32,8);
	//可以自行计算[结果在下方三行处]


    //将得到->abca[0]=0x78 [1]=0x56 [2]=0x34 [3]=0x12 [4]=0x13 [5]=0x12 [6]=0x11 [7]=0x90

本项目代码

#include <WinSock2.h>
#include <QDebug>
#include <iostream>
#pragma comment(lib,"ws2_32.lib")
bool isLittleEndian() {
    uint16_t number = 0x1234;
    uint8_t *ptr = reinterpret_cast<uint8_t*>(&number);
    return (ptr[0] == 0x34);
}
void MainWindow::reverseArray(char *src, char *dest, uint length)
{
    for(uint i=0; i<length/4; i++)
    {
        dest[4*i+0] = src[4*i+3];
        dest[4*i+1] = src[4*i+2];
        dest[4*i+2] = src[4*i+1];
        dest[4*i+3] = src[4*i];
    }
}
/**
 * 前
 * 言:
 * 在讨论 memset 和 memcpy 函数时,它们的最小单位是以字节(byte)为单位。
 *
 * 为什么要区分 大小端字节序
 * 如:0x12345678(此种写法 左侧高字节 右侧低字节)
 * 地址显示: 0000 0001 0002 0003
 * 小端显示: 78   56   34   12
 * 大端显示: 12   34   56   78
 * 我们知道当我们 memset或memcpy从内存读数时都是从低地址往出读的
 * 注意:我们读出来数据时是从低位读的,
 * 那么小端模式下(读出来的低地址数据在低位):读出来的数写到 目的地址的时候也是依次向右挤的(头插法)
 * 在大端模式下(读出来的低地址数据在高位):读出来的数写到 目的地址的时候是依次向右添加的(尾插法)
 * 此时当我们memset 1字节时读出来的是:
 * 小端读小端:0x 78
 * 大端读大端:0x 12
 * 小端机器上读大端数据0x 12
 *  * 此时当我们memset 2字节时读出来的是:
 * 小端读小端:0x 56 78
 * 大端读大端:0x 12 34
 * 小端机器上读大端数据:0x 34 12
 *  * 此时当我们memset 3字节时读出来的是:
 * 小端读小端:0x 34 56 78
 * 大端读大端:0x 12 34 56
 * 小端机器上读大端数据:0x 56 34 12
 *  * 此时当我们memset 4字节时读出来的是:
 * 小端读小端:0x 12 34 56 78
 * 大端读大端:0x 12 34 56 78
 * 小端机器上读大端数据:0x 78 56 34 12
 * */
void MainWindow::on_pushButton_5_clicked()
{
    <地址从左到右依次递增
#pragma pack(1)
    char _32low[] ={ 0x12,0x34,0x56,0x78,0x90,0x11,0x12,0x13};//小端
    uint32_t _32low_t ;
    memcpy(&_32low_t,_32low+0,4);//由于小端 memcpy之后变为0x78563412
    uint32_t _32high1,_32high2,_32high3,_32high4 ;
    uint8_t _81;
    uint16_t _161;
    uint32_t _321;

    char abca[8];

    reverseArray((char*)&_32low,abca,8);//这里模拟讲小端转为大端 变为{0x78,0x56,0x34,0x12,0x13,0x12,0x11,0x90}
    memcpy(&_32high1,abca+0,1);//由于我们是小端机器,那么memcpy后变成 0x78
    memcpy(&_32high2,abca+0,2);//由于我们是小端机器,那么memcpy后变成 0x5678
    memcpy(&_32high3,abca+0,3);//由于我们是小端机器,那么memcpy后变成 0x345678
    memcpy(&_32high4,abca+0,4);//由于我们是小端机器,那么memcpy后变成 0x12345678

    memcpy(&_81,abca+2,1);//由于我们是小端机器,那么memcpy后变成 0x34
    memcpy(&_161,abca+2,2);//由于我们是小端机器,那么memcpy后变成 0x1234
    memcpy(&_321,abca+2,4);//由于我们是小端机器,那么memcpy后变成 0x12131234

    memcpy(abca,_32low,8);//abca得到的是[0]=0x12 [1]=0x34 。。。原因是从低地址memcpy到高地址
#pragma pack(0)

#pragma pack(1)
    uint32_t u32[8]={0x12345678,0x90111213,0x14151617,0x18192021,0x22232425,0x26272829,0x30313233,0x34353637};
#pragma pack(0)
    uint32_t u32new[8];
    memcpy(u32new,u32,sizeof(uint32_t)*8);
    //分析一下
    //地址 1000 1001 1002 1003
    //数据 0x13 0x12 0x11 0x90
    //memcpy时我们是小端的机器所以从小端
    memcpy(&_321,u32new+1,4);//因为我们小端设备,所以memcpy时会从低地址读写到我们的低地址 所以写出来是0x90111213

    memcpy(abca,u32,8);
    //将得到->abca[0]=0x78 [1]=0x56 [2]=0x34 [3]=0x12 [4]=0x13 [5]=0x12 [6]=0x11 [7]=0x90

    qDebug()<<"_32low = "<<QString::number(_32low_t,16);
    qDebug()<<"_32high ="<<QString::number(_32high4,16);
    if (isLittleEndian()) {
        std::cout << "This system is Little Endian" << std::endl;
    } else {
        std::cout << "This system is Big Endian" << std::endl;
    }

    for(int i=0 ; i<10 ; i++)
    {
        static uint ss = 0;
        ss++;
        qDebug()<<ss;
    }

}

结语


  • 如有问题欢迎指正

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值