当前位置: 首页 > news >正文

Qt|大小端数据转换

后面打算写Qt关于网络编程的博客,网络编程就绕不开字节流数据传输,字节流数据的传输一般是根据协议来定义对应的报文该如何组包,那这就必然牵扯到了大端字节序和小端字节序的问题了。不清楚的大小端的可以看一下相关资料:大小端模式_百度百科 (baidu.com)。

这里看一个具体的例子比如某个报文协议是这样定的:

报头...
设备编号U16(两个字节)
设备温度U16(两个字节)
设备湿度U16(两个字节)
设备状态U16(两个字节)
报尾...

那么传输过程的报文结构,除去头尾之外应该是这样的:

代码报文结构体这样定义

struct DeviceData {quint16 number;quint16 temperature;quint16 humidness;quint16 status;
};

然后简单测试一下

  QByteArray data;DeviceData d;d.number = 0x1234;d.temperature = 0x5678;d.humidness = 0x4321;d.status = 0x8756;data.append(reinterpret_cast<char *>(&d), sizeof(DeviceData));qDebug() << data.toHex();

编译运行查看一下打印结果:

如果看了前面关于大小端的资料应该就会明白为什么这里打印结果是“3412785621435687”,这里也有一个参考的文章:大小端格式由编译器,操作系统还是CPU决定的?答案是CPU_大端cpu采用小端编译链-CSDN博客我的机器cpu,如果不采取任何处理,这里输出的确实是小端数据,如果是大端那么就会输出对应的“1234567843218756”。如果上文中举例的协议定的就是小端数据传输那么就是这样写,无需做任何处理。如果是大端数据传输则需要做对应的处理。同样上文中还存在一个问题,比如设备编号协议里面定的是无符号一个字节即uint8类型的。那么结构体将会这样定义:

struct DeviceData {quint8 number;quint16 temperature;quint16 humidness;quint16 status;
};

测试代码:

  QByteArray data;DeviceData d;d.number = 0x12;d.temperature = 0x5678;d.humidness = 0x4321;d.status = 0x8756;data.append(reinterpret_cast<char *>(&d), sizeof(DeviceData));qDebug() << data.toHex();

这里将结构体转QByteArray使用了reinterpret_cast,可以自己查一下static_castdymatic_castreinterpret_cast以及Qt的qobject_cast有什么区别。同样还可以使用QByteArray的setRawData方法:

QByteArray data;
data.setRawData(reinterpret_cast<char *>(&d), sizeof(DeviceData));

结果也是一样的。

将对应QByteArray转回结构体直接使用memcpy即可。例如上面的例子:

DeviceData dd;

memcpy(&dd, data.constData(), sizeof(DeviceData));

打印输出:

为什么打印是这样,可以先看看这个结构体的大小,打印 sizeof(DeviceData)可以看到是8个字节,结构体成员一个quint8,三个quint16,大小:1+2+2+2为什么是8而不是7, 这个就需要了解关于字节对齐的知识了:字节对齐_百度百科 (baidu.com)

直接采用1字节对齐:

#pragma pack(push, 1);
struct DeviceData {quint8 number;quint16 temperature;quint16 humidness;quint16 status;
};
#pragma pack(pop);

再查看打印:

然后sizeof(DeviceData)也是7了(这里使用1字节对齐会影响效率)。·

回到关于大小端的问题,代码里面采用的是结构体转QByteArray,这样就牵扯到了依靠系统自己的大小端来处理了,代码不灵活。可以写一个通用的方法来根据需求转换对应需要的字节序。

方法一:

思路是一个字节一个字节进行拷贝使用QByteArray的append方法以及位移操作。

比如一个无符号四字节的quint32 u32=0x12345678(大端就是12345678,小端就是78563412),先执行下列代码:

  quint32 u32 = 0x12345678;QByteArray data;data.append(u32);qDebug() << data.toHex();

查看打印:

也就是说QByteArray的 append方法在这种情况下并不会将整数 u32 转换为字节流,而是将整数的低字节(最低有效字节)追加到 QByteArray中。

转化为小端数据:

原生数据操作原生数据QByteArray进行append追加
12345678右移0位1234567878
12345678右移8位001234567856
00123456右移16位00001234785634
00001234右移24位0000001278563412

转化为大端数据:

原生数据操作原生数据QByteArray进行append追加
12345678右移24位0000001212
12345678右移16位000012341234
00123456右移8位00123456123456
00001234右移0位1234567812345678

对应代码:

  quint32 u32 = 0x12345678;//输出小端数据QByteArray littleEndian;littleEndian.append(u32);littleEndian.append(u32 >> 8);littleEndian.append(u32 >> 16);littleEndian.append(u32 >> 24);qDebug() << "little:" << littleEndian.toHex();//输出大端数据QByteArray bigEndian;bigEndian.append(u32 >> 24);bigEndian.append(u32 >> 16);bigEndian.append(u32 >> 8);bigEndian.append(u32);qDebug() << "bigEndian:" << bigEndian.toHex();

编译运行查看打印:

 对应转回同理,下面是完整代码:

  quint32 u32 = 0x12345678;//输出小端数据QByteArray littleEndian;littleEndian.append(u32);littleEndian.append(u32 >> 8);littleEndian.append(u32 >> 16);littleEndian.append(u32 >> 24);qDebug() << "littleEndian:" << littleEndian.toHex();quint32 u32x = 0;u32x |= static_cast<quint8>(littleEndian[0]);u32x |= (static_cast<quint8>(littleEndian[1]) << 8);u32x |= (static_cast<quint8>(littleEndian[2]) << 16);u32x |= (static_cast<quint8>(littleEndian[3]) << 24);qDebug() << "ori data:" << u32x;//输出大端数据QByteArray bigEndian;bigEndian.append(u32 >> 24);bigEndian.append(u32 >> 16);bigEndian.append(u32 >> 8);bigEndian.append(u32);qDebug() << "bigEndian:" << bigEndian.toHex();quint32 u32y = 0;u32y |= (static_cast<quint8>(bigEndian[0]) << 24);u32y |= (static_cast<quint8>(bigEndian[1]) << 16);u32y |= (static_cast<quint8>(bigEndian[2]) << 8);u32y |= static_cast<quint8>(bigEndian[3]);qDebug() << "ori data:" << u32y;

编译运行查看打印:

305419896对应的16进制就是0x12345678:

其他数据类型同理,这里写成模板函数:

template <typename T>
static QByteArray toData(const T &value, bool isLittle) {QByteArray data;for (int i = 0; i < sizeof(T); ++i) {int bitOffset = (isLittle) ? i : sizeof(T) - i - 1;data.append(value >> bitOffset * 8);}return data;
}template <typename T>
static void fromData(const QByteArray &data, bool isLittle, T &value) {for (int i = 0; i < sizeof(T); ++i) {int bitOffset = (isLittle) ? i : sizeof(T) - i - 1;value |= (static_cast<quint8>(data[i]) << bitOffset * 8);}
}

上面例子代码改为:

  quint32 u32 = 0x12345678;//输出小端数据QByteArray littleEndian = toData(u32, true);qDebug() << "littleEndian:" << littleEndian.toHex();quint32 u32x = 0;fromData(littleEndian, true, u32x);qDebug() << "ori data:" << u32x;//输出大端数据QByteArray bigEndian = toData(u32, false);qDebug() << "bigEndian:" << bigEndian.toHex();quint32 u32y = 0;fromData(bigEndian, false, u32y);qDebug() << "ori data:" << u32y;

编译运行查看打印:

跟前面的一致。

方法二:

思路是使用QDataStream的读写数据,然后借助QDataStream的setByteOrder方法,具体就不多细讲,直接看模板函数:

template <typename T>
QByteArray toData1(T value, bool isLittle) {QByteArray data;QDataStream stream(&data, QIODevice::WriteOnly);if (isLittle)stream.setByteOrder(QDataStream::LittleEndian);elsestream.setByteOrder(QDataStream::BigEndian);stream << value;return data;
}template <typename T>
void fromData1(const QByteArray &data, bool isLittle, T &value) {QDataStream stream(data);if (isLittle)stream.setByteOrder(QDataStream::LittleEndian);elsestream.setByteOrder(QDataStream::BigEndian);stream >> value;
}

将上面例子改为使用这两个方法:

  quint32 u32 = 0x12345678;//输出小端数据QByteArray littleEndian = toData1(u32, true);qDebug() << "littleEndian:" << littleEndian.toHex();quint32 u32x = 0;fromData1(littleEndian, true, u32x);qDebug() << "ori data:" << u32x;//输出大端数据QByteArray bigEndian = toData1(u32, false);qDebug() << "bigEndian:" << bigEndian.toHex();quint32 u32y = 0;fromData1(bigEndian, false, u32y);qDebug() << "ori data:" << u32y;

编译运行查看打印:

与方法一结果一致。

方法三:

借助Qt的QtEndian:

首先qt有判断当前CPU是大端还是小端的宏:

例如:

#if Q_BYTE_ORDER == Q_BIG_ENDIANqDebug() << "current endian is big";
#endif#if Q_BYTE_ORDER == Q_LITTLE_ENDIANqDebug() << "current endian is little";
#endif

编译运行查看打印:

因为我的是x86是小端。

对应Qt也有一些大小端转换的方法:

具体使用有兴趣的可以探究一下,我没有过多研究Qt的这个。 

相关文章:

Qt|大小端数据转换

后面打算写Qt关于网络编程的博客&#xff0c;网络编程就绕不开字节流数据传输&#xff0c;字节流数据的传输一般是根据协议来定义对应的报文该如何组包&#xff0c;那这就必然牵扯到了大端字节序和小端字节序的问题了。不清楚的大小端的可以看一下相关资料&#xff1a;大小端模…...

禅道添加自定义字段

1&#xff0c;数据库表 zt_story 添加自定义字段 bakDate1&#xff0c;bakDate2&#xff0c;bakDate3&#xff0c;bakDate4 2&#xff0c;在 /opt/lampp/htdocs/zentaopms/extension/custom/story/ext/config 中添加bakDate.php文件 <?php $config->story->datatab…...

蓝桥杯2024/1/26笔记-----基于PCF8591的电压采集装置

功能实现要求&#xff1a; 每次建好工程文件夹&#xff0c;里边包含User&#xff08;放工程文件&#xff0c;mian.c&#xff0c;可以在这里写如同我这个文章的文本文档&#xff09;、Driver&#xff08;存放底层文件如Led.c&#xff0c;Led.h等&#xff09; 新建的工程先搭建框…...

【一】esp32芯片开发板环境搭建

1、esp32的源码在github上的地址 不同的芯片支持的源码版本不一样&#xff0c;需要根据自己的实际的esp32开发板的芯片下载不用版本的代码 esp32支持多种开发方式&#xff0c;如arduino&#xff0c;ESP-IDF等。官方推荐使用idf开发&#xff0c;ESP-IDF 是乐鑫官方推出的物联网开…...

PyTorch2ONNX-分类模型:速度比较(固定维度、动态维度)、精度比较

图像分类模型部署: PyTorch -> ONNX 1. 模型部署介绍 1.1 人工智能开发部署全流程 #mermaid-svg-bAJun9u4XeSykIbg {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-bAJun9u4XeSykIbg .error-icon{fill:#552222;}…...

Docker命令快车道:一票通往高效开发之旅

欢迎登上 Docker 命令快车&#xff01;在这趟旅程中&#xff0c;你不仅会学会如何驾驭 Docker 这辆神奇的车&#xff0c;还会发现如何让你的开发旅程变得更加轻松愉快。现在&#xff0c;请系好安全带&#xff0c;我们即将出发&#xff01; Docker 是什么 Docker 就像是一辆超…...

IP类接口大全,含免费次数

IP查询 IP归属地-IPv4高精版&#xff1a;根据IP地址查询归属地信息&#xff0c;支持到中国地区&#xff08;不含港台地区&#xff09;街道级别&#xff0c;包含国家、省、市、区县、详细地址和运营商等信息。IP归属地-IPv4区县级&#xff1a;根据IP地址查询归属地信息&#xf…...

LLMs 的记忆和信息检索服务器 Motorhead

LLMs 的记忆和信息检索服务器 Motorhead 1. 为什么使用 Motorhead&#xff1f;2. 通过 Docker 启动 Motorhead3. Github 地址4. python 使用示例地址 1. 为什么使用 Motorhead&#xff1f; 使用 LLMs构建聊天应用程序时&#xff0c;每次都必须构建记忆处理。Motorhead是协助该…...

vue3项目中让echarts适应div的大小变化,跟随div的大小改变图表大小

目录如下 我的项目环境如下利用element-resize-detector插件监听元素大小变化element-resize-detector插件的用法完整代码如下&#xff1a;结果如下 在做项目的时候&#xff0c;经常会使用到echarts&#xff0c;特别是在做一些大屏项目的时候。有时候我们是需要根据div的大小改…...

springboot启动异常

Error creating bean with name ‘dataSource’ org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name dataSource: Unsatisfied dependency expressed through field basicProperties; nested exception is org.springframew…...

直播主播之互动率与促单

直播互动率是衡量直播间观众参与度的重要指标&#xff0c;通常指的是直播间的观众点赞、评论以及转发的数量。互动率越高&#xff0c;表明观众参与度越高&#xff0c;直播间的人气值也相应越高。 为了提升直播互动率&#xff0c;主播可以采取以下策略: 1.积极引导观众参与互动…...

Android 基础技术——Bitmap

笔者希望做一个系列&#xff0c;整理 Android 基础技术&#xff0c;本章是关于 Bitmap Bitmap 内存如何计算 占用内存 宽 * 缩放比例 * 高 * 缩放比例 * 每个像素所占字节 缩放比例 设备dpi/图片所在目录的dpi Bitmap加载优化&#xff1f;不改变图片质量的情况下怎么优化&am…...

数据结构奇妙旅程之七大排序

꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN …...

【JavaScript】Generator

MDN-Generator Generator对象由生成器函数返回&#xff0c;并且它符合可迭代协议和迭代器协议。 Generator-核心语法 核心语法: 定义生成器函数获取generator对象yield表达式的使用通过for of获取每一个yield的值 // 1. 通过function* 创建生成器函数 function* foo() {//…...

河南省考后天网上确认,请提前准备证件照哦

✔报名时间&#xff1a;2024年1月18号一1月24号 ✔报名确认和缴费&#xff1a;2024年1月 31号一2月4号 ✔准考证打印&#xff1a;2024年3月12号一3月17号 ✔笔试时间&#xff1a;2024年3月16日-2024年3月17日。 ✔面试时间&#xff1a;面试时间拟安排在2024年5月中旬 报名网址&…...

【前端】防抖和节流

防抖 防抖用于限制连续触发的事件的执行频率。当一个事件被触发时,防抖会延迟一定的时间执行对应的处理函数。如果在延迟时间内再次触发了同样的事件,那么之前的延迟执行将被取消,重新开始计时。 总结:在单位时间内频繁触发事件,只有最后一次生效 场景 :用户在输入框输…...

【网络】:网络套接字(UDP)

网络套接字 一.网络字节序二.端口号三.socket1.常见的API2.封装UdpSocket 四.地址转换函数 网络通信的本质就是进程间通信。 一.网络字节序 我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分,网…...

Linux编程 1/2 数据结构

数据结构: 程序 数据结构 算法 1.数据结构: 1.时间复杂度: 数据量的增长与程序运行时间增长所呈现的比例函数,则称为时间渐进复杂度函数简称时间复杂度 O(c) > O(logn)> O(n) > O(nlogn) > O(n^2) > O(n^3) > O(2^n) 2.空间复杂度: 2.类…...

【UE Niagara】实现闪电粒子效果的两种方式

目录 效果 步骤 方式一&#xff08;网格体渲染器&#xff09; &#xff08;1&#xff09;添加网格体渲染器 &#xff08;2&#xff09;修改粒子显示方向 &#xff08;3&#xff09;添加从上到下逐渐显现的效果 &#xff08;4&#xff09;粒子颜色变化 方式二&#xff0…...

js数组/对象的深拷贝与浅拷贝

文章目录 一、js中的深拷贝和浅拷贝二、浅拷贝1、Object.assign()2、利用es6扩展运算符&#xff08;...&#xff09; 二、深拷贝1、JSON 序列化和反序列化2、js原生代码实现3、使用第三方库lodash等 四、总结 一、js中的深拷贝和浅拷贝 在JS中&#xff0c;深拷贝和浅拷贝是针对…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接&#xff1a;3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

电脑插入多块移动硬盘后经常出现卡顿和蓝屏

当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时&#xff0c;可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案&#xff1a; 1. 检查电源供电问题 问题原因&#xff1a;多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

什么是库存周转?如何用进销存系统提高库存周转率?

你可能听说过这样一句话&#xff1a; “利润不是赚出来的&#xff0c;是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业&#xff0c;很多企业看着销售不错&#xff0c;账上却没钱、利润也不见了&#xff0c;一翻库存才发现&#xff1a; 一堆卖不动的旧货…...

el-switch文字内置

el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析&#xff08;97/126&#xff09;&#xff1a;邮件营销与用户参与度的关键指标优化指南 在数字化营销时代&#xff0c;邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天&#xff0c;我们将深入解析邮件打开率、网站可用性、页面参与时…...

ABAP设计模式之---“简单设计原则(Simple Design)”

“Simple Design”&#xff08;简单设计&#xff09;是软件开发中的一个重要理念&#xff0c;倡导以最简单的方式实现软件功能&#xff0c;以确保代码清晰易懂、易维护&#xff0c;并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计&#xff0c;遵循“让事情保…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP

编辑-虚拟网络编辑器-更改设置 选择桥接模式&#xff0c;然后找到相应的网卡&#xff08;可以查看自己本机的网络连接&#xff09; windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置&#xff0c;选择刚才配置的桥接模式 静态ip设置&#xff1a; 我用的ubuntu24桌…...

JVM 内存结构 详解

内存结构 运行时数据区&#xff1a; Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器&#xff1a; ​ 线程私有&#xff0c;程序控制流的指示器&#xff0c;分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 ​ 每个线程都有一个程序计数…...

QT3D学习笔记——圆台、圆锥

类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体&#xff08;对象或容器&#xff09;QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质&#xff08;定义颜色、反光等&#xff09;QFirstPersonC…...