数据大小端问题

文章目录

    • 大小端
      • 前言
      • 函数引用(接下来使用此函数对高低位进行切换)
      • 先看截取的对于大小端的定义
      • 大小端数据的直观理解[重点]
    • 对uchar数组进行取操作
      • 定义一个uint8_t的数组观察起内部内存
      • 尝试使用uint32_t 每次区 1、2、3、4byte数据
    • 提升
    • 经过上面的介绍一定对大小端有了一定的了解,那么接下来进行的是 memcpy数组到另一个数组
      • uchar[]给uchar[]
      • uint32_t[]给uint32_t[]
      • uint32_t[]给uint8_t[]
    • 本项目代码
    • 结语

大小端

前言


在讨论大小端前我们先要了解: 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;
    }

}

结语


  • 如有问题欢迎指正

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/768758.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2.3 主程序和外部IO交互 (文件映射方式)----IO Server实现

2.3 主程序和外部IO交互 &#xff08;文件映射方式&#xff09;----IO Server C实现 效果显示 1 内存共享概念 基本原理&#xff1a;以页面为单位&#xff0c;将一个普通文件映射到内存中&#xff0c;达到共享内存和节约内存的目的&#xff0c;通常在需要对文件进行频繁读写时…

基于OpenMV识别数字及程序说明

OpenMV简介 OpenMV是一个开源、低成本且功能强大的机器视觉模块。它基于STM32F427CPU&#xff0c;集成了OV7725摄像头芯片&#xff0c;能在小巧的硬件模块上&#xff0c;用C语言高效地实现核心机器视觉算法&#xff0c;并提供了Python编程接口&#xff0c;使得图像处理的复杂度…

【教程】lighttpd配置端口反向代理

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 1、修改配置文件&#xff1a; sudo vim /etc/lighttpd/lighttpd.conf2、先添加mod_proxy&#xff1a; 3、然后添加端口映射&#xff1a; 4、保存&…

2024年07年01日 Redis数据类型以及使用场景

String Hash List Set Sorted Set String&#xff0c;用的最多&#xff0c;对象序列化成json然后存储 1.对象缓存&#xff0c;单值缓存 2.分布式锁 Hash&#xff0c;不怎么用到 1.可缓存经常需要修改值的对象&#xff0c;可单独对对象某个属性进行修改 HMSET user {userI…

Transformation(转换)开发-switch/case组件

一、switch/case组件-条件判断 体育老师要做一件非常重要的事情&#xff1a;判断学生是男孩还是女孩、或者是蜘蛛&#xff0c;然后让他们各自到指定的队伍中 体育老师做的事情&#xff0c;我们同样也会在Kettle中会经常用来。在Kettle中&#xff0c;switch/case组件可以来做类似…

河南特岗教师报名流程及免冠照片电子版制作要求

2024年河南特岗教师招聘季又来啦&#xff01;想要成为孩子们心中的超级英雄吗&#xff1f;想要在教育的田野上播种希望吗&#xff1f;那就不要错过这次机会&#xff0c;今年全省共招聘特岗教师3495名&#xff08;具体岗位设置参见《河南省2024年特岗教师招聘岗位设置》&#xf…

盒子模型(笔记)

盒子模型 盒子模型的属性 padding属性 内边距&#xff1a;盒子的边框到内容的距离 /*每个方向内边距*/padding-top: 20px;padding-left:20px;padding-bottom:20px;padding-right: 20px; /*每个方向内边距的第二种方法*/ /* 顺序依次是上左右下*/padding: 10px 20px 30px 4…

02:vim的使用和权限管控

vim的使用 1、vim基础使用1.1、vim pathname 2、vim高级用法2.1、查找2.2、设置显示行号2.3、快速切换行2.4、 行删除2.5、行复制粘贴 3、权限管理3.1、普通用户和特权用户3.2、文件权限表示 vim是Linux中的一种编辑器&#xff0c;类似于window中的记事本&#xff0c;可以对创建…

TP8/6 更改后台入口地址admin改为myadmin 隐藏真实后台网址

原来www.xxx.com/admin 改后www.xxx.com/myadmin config/app.php // 应用映射&#xff08;自动多应用模式有效&#xff09;app_map > [admintest>admin],

【前端VUE】VUE3第一节—vite创建vue3工程

什么是VUE Vue (发音为 /vjuː/&#xff0c;类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff0c;帮助你高效地开发用户界面。无论是简单还是复杂的界面&#xff0…

卷积层里的填充和步幅

一、定义 1、对于卷积&#xff0c;我们另一个超参数是核的大小&#xff0c;通常使用的卷积核是33或者55&#xff0c;很少用偶数核 2、填充是为了让输出不变或者变大&#xff0c;是为了在输入不太大&#xff0c;又能使模型足够深的情况下使用 3、填充&#xff1a;在输入周围添…

开源软件开发平台哪家好?

进行数字化转型&#xff0c;离不开低代码技术平台等软件产品的加持与助力。因为它更好操作、更灵活、易维护等优势特点突出&#xff0c;在推动企业实现流程化办公的过程中助力明显&#xff0c;作用大&#xff0c;深得客户喜爱。那么&#xff0c;低代码技术平台、开源软件开发平…

居然这么简单就能实现扫雷游戏!

目录 一.思路 1.成果展示 2.思路 二.具体操作 1.创建"棋盘" 2.初始化雷 3.布置雷 4.打印 5.排除雷 三.代码实现 1.test.c文件 2.thunder.h文件 3.thunder.c文件 Hello&#xff0c;大家好&#xff0c;今天我们来实现扫雷游戏&#xff0c;希望这一篇博客能给带给大家一…

HCIA是什么等级的证书

HCIA是华为认证体系中的初级认证&#xff0c;主要用于表明持有者在某一技术领域达到了工程师级别的能力证明。这种认证主要涉及到ICT(信息技术基础设施与通信)设备的安装、配置、运行以及故障排除等方面。 HCIA证书的作用 HCIA证书是进入IT行业的一个初步资格证书&#xff0c;它…

昇思25天学习打卡营第6天|数据变换 Transforms

学习目标&#xff1a;熟练掌握数据变换操作 熟悉mindspore.dataset.transforms接口 实践掌握常用变换 昇思大模型平台学习心得记录&#xff1a; 一、关于mindspore.dataset.transforms 1.1 变换 mindspore.dataset.transforms.Compose将多个数据增强操作组合使用。 mindspo…

【Java】如果让你设计一个分布式链路跟踪系统?你怎么做?

一、问题解析 分布式链路跟踪服务属于写多读少的服务&#xff0c;是我们线上排查问题的重要支撑。我经历过的一个系统&#xff0c;同时支持着多条业务线&#xff0c;实际用上的服务器有两百台左右&#xff0c;这种量级的系统想排查故障&#xff0c;难度可想而知。 因此&#…

Linux文件传输

目录 wget传输 先用我的云服务器vps开放一个端口 目标机wget进行请求下载 指定保存路径 curl传输&#xff08;win11也有&#xff09; ​Netcat传输 文件传输 SFTP传输 连接远程主机 下载远程主机文件 DNS传输 dns带出字符数据命令 拼接hex码进行转字符串 成功转出​…

WIN32核心编程 - 数据类型 错误处理 字符处理

公开视频 -> 链接点击跳转公开课程博客首页 -> 链接点击跳转博客主页 目录 数据类型 基本数据类型 Win32基本数据类型 错误处理 C语言中的错误处理 C中的错误处理 Win32中的错误处理 字符处理 C/C WIN32 字符处理 数据类型 基本数据类型 C/C语言定义了一系列…

umi项目中的一些趣事

前言 出于某些安全问题&#xff0c;需要把HTML中框架注入的umi版本信息去掉&#xff0c;那要怎么搞呢~ 方案 查找官方文档&#xff0c;没发现可以去掉注入信息的方法&#xff0c;但在一番折腾后&#x1f609;终究还是解决了~ 发现 版本信息是从这里注入的~ Object.define…

uboo对内存操作读写命令的基本使用

内存操作命令 直接对DRAM进行读写的操作,uboot常用的内存操作命令有md,nm,mm,mw,cp和cmp. md命令: 显示内存值 # md md - memory display Usage: md [.b, .w, .l, .q] address [# of objects] b:1个字节 byte w:2个字节 world l:4个字节 long of objects 以word 为单位的1…