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);
  else
    stream.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);
  else
    stream.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_ENDIAN
  qDebug() << "current endian is big";
#endif

#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
  qDebug() << "current endian is little";
#endif

编译运行查看打印:

因为我的是x86是小端。

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

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

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

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

相关文章

jenkins对接K8S

创建连接K8S的凭据 查看需要使用到的命名空间 [rootk8s ~]# kubectl get ns |grep arts-system arts-system Active 16d创建service accounts [rootk8s ~]# kubectl create sa jenkins-k8s -n arts-system serviceaccount/jenkins-k8s created [rootk8s ~]# kubectl…

log4j2 配置入门介绍

配置 将日志请求插入到应用程序代码中需要进行大量的计划和工作。 观察表明&#xff0c;大约4%的代码专门用于日志记录。因此&#xff0c;即使是中等规模的应用程序也会在其代码中嵌入数千条日志记录语句。 考虑到它们的数量&#xff0c;必须管理这些日志语句&#xff0c;而…

CTF CRYPTO 密码学-7

题目名称&#xff1a;敲击 题目描述&#xff1a; 让我们回到最开始的地方 0110011001101100011000010110011101111011011000110110010100110011011001010011010100110000001100100110001100101101001101000011100001100011001110010010110100110100011001000011010100110000…

python简单socket demo

socket说明 socket本质是编程接口(API)&#xff0c;对TCP/IP的封装&#xff0c;TCP/IP也要提供可供程序员做网络开发所用的接口&#xff0c;这就是Socket编程接口。除了常见的http请求之外&#xff0c;一些敏感的数据传输常用socket套接字层直接传输数据。一个简单的domo用于熟…

构造器模式

构造器模式 意图 将一个复杂对象的构建和表示分离&#xff0c;使得相同的构建能创建不同的表示。 解释 案例&#xff1a;想象一个角色扮演游戏的特征生成器。最简单的选择是让计算机为你创建角色。如果你想手动选择特征的细节像职业、性别、头发的颜色等。特征的产生是一个循…

【golang】16、dlv 调试工具、vscode+ssh 远程调试

文章目录 Goland Debug 模式崩溃 Goland Debug 模式崩溃 有时遇到如下现象&#xff1a; Golang Run 模式正常&#xff0c;Debug 无 BreakPoint 模式正常&#xff0c;但 Debug 加 BreakPoint 就会偶现 panic&#xff0c;panic 信息如下。 panic: runtime error: index out of …

敲黑板啦!CSGO游戏搬砖项目操作注意事项

CSGO游戏搬砖项目怎么赚钱的&#xff0c;利润在哪&#xff1f; 1.两个平台之间币种不一样&#xff0c;就存在一个汇率差&#xff0c;两平台装备价格也不一样&#xff0c;汇率差-价格差利润。 CSGO游戏搬砖项目具体有哪些操作步骤&#xff1f; 1、准备一台电脑&#xff0c;配置…

操作系统(7)----调度相关知识点(万字总结~)

目录 一.调度的三个层次 1.高级调度 2.低级调度 3.中级调度 二.进程的挂起状态 三.进程调度的时机 四.进程调度方式 1.非剥夺调度方式 2.剥夺调度方式 五.进程的切换与过程 六.调度器/调度程序 1.调度程序 2.闲逛进程 七.评价调度算法的各个指标 1.CPU利用率 2…

yarn安装第三方插件包,提示报错,yarn的镜像源已经过期了,因为yarn和npm用的是淘宝的镜像源,淘宝的镜像源已经过期了,要设置最新的淘宝镜像源。

淘宝最新镜像源切换_淘宝镜像-CSDN博客 查看yarn用的什么镜像源 yarn config get registry 查看具体的信息 yarn config list 设置淘宝的最新镜像源&#xff0c;yarn和npm都要设置最新的淘宝镜像源&#xff0c;不然还是报错 npm config set registry https://registry.npmm…

Mysql-存储引擎-InnoDB

数据文件 下面这条SQL语句执行的时候指定了ENGINE InnoDB存储引擎为InnoDB: CREATE TABLE tb_album (id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 编号,title varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 相册名称,image varc…

leetcode26. 删除有序数组中的重复项

题目 题目 给你一个 非严格递增排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。 考虑 nums 的唯一元素的数量为 k &…

python笔记10

1、继承 继承是面向对象编程中的一个重要概念&#xff0c;它允许一个类&#xff08;子类&#xff09;继承另一个类&#xff08;父类&#xff09;的属性和方法。通过继承&#xff0c;子类可以重用父类的代码&#xff0c;并且有机会添加新的属性和方法&#xff0c;或者重写父类的…

【HTML 基础】元素和标签

文章目录 1. <p> - 段落标签2. <h1> - <h6> - 标题标签3. <a> - 超链接标签4. <img> - 图片标签5. <ul>, <ol>, <li> - 列表标签无序列表有序列表 总结 HTML&#xff08;Hypertext Markup Language&#xff09;是构建 Web 页面…

RabbitMQ快速实战

目录 什么是消息队列&#xff1f; 消息队列的优势 应用解耦 异步提速 削峰填谷 总结 主流MQ产品特点比较 Rabbitmq快速上手 创建用户admin Exchange和Queue Connection和Channel RabbitMQ中的核心概念总结 什么是消息队列&#xff1f; MQ全称Message Queue&#xf…

某大厂关于Linux系统相关面试题

一、Linux系统和Shell 1、写一个sed命令&#xff0c;修改/tmp/input.txt文件的内容&#xff0c;要求&#xff1a;(1) 删除所有空行&#xff1b;(2) 在非空行前面加一个"AAA"&#xff0c;在行尾加一个"BBB"&#xff0c;即将内容为11111的一行改为&#xff1…

linux 下scrcpy 手机投屏到电脑,QT+ffmpeg 获取视频流,处理等等

linux 下scrcpy 手机投屏到电脑,QT+ffmpeg 获取视频流,处理 1 安装 scrcpy 地址 https://github.com/Genymobile/scrcpy 转到 relese 下载 我这里下载的是linux系统 v2.3.1 版本 scrcpy-2.3.1.tar.gz 下载 scrcpy-server v2.3.1 版本 scrcpy-server-v2.3.1 解压scrcpy-2.3…

【工具推荐】磁盘分析工具 | SpaceSniffer 高效分析

文章目录 1 下载2 分析 最近发现一款很好用的工具——SpaceSniffer&#xff08;磁盘空间分析工具&#xff09; 硬盘用久了&#xff0c;里头的文件总是杂乱不堪&#xff0c;十分影响效率和心情&#xff1b; 我们往往会忘记哪些内容占用的空间比较多~所以我们如果可以高效分析哪…

Linux 网络流量相关工具

本文聚焦于网络流量的查看、端口占用查看。至于网络设备的管理和配置&#xff0c;因为太过复杂且不同发行版有较大差异&#xff0c;这里就不赘述&#xff0c;后面看情况再写。 需要注意的是&#xff0c;这里列出的每一个工具都有丰富的功能&#xff0c;流量/端口信息查看只是其…

深度学习-使用Labelimg数据标注

数据标注是计算机视觉和机器学习项目中至关重要的一步&#xff0c;而使用工具进行标注是提高效率的关键。本文介绍了LabelImg&#xff0c;一款常用的开源图像标注工具。用户可以在图像中方便而准确地标注目标区域&#xff0c;为训练机器学习模型提供高质量的标注数据。LabelImg…

软件设计师-23年上半年-下午试题

软件设计师-23年上半年-下午试题 更多软考资料 https://ruankao.blog.csdn.net/试题一、二、三、四必答&#xff0c;五、六二选一 试题一(15分) 说明 随着农业领域科学种植的发展&#xff0c;需要对农业基地及农事进行的信息化管理&#xff0c;为租户和农户等人员提供种植相…